diff --git a/plugins/platforms/photon/sidecar/index.mjs b/plugins/platforms/photon/sidecar/index.mjs index 85c3aa287366..b6aa63f19c9e 100644 --- a/plugins/platforms/photon/sidecar/index.mjs +++ b/plugins/platforms/photon/sidecar/index.mjs @@ -343,6 +343,31 @@ async function normalizeEvent(space, message) { } } +function inboundStreamErrorMessage(e) { + const msg = e && e.message ? e.message : String(e); + let out = "photon-sidecar: inbound stream errored — restarting: " + msg; + + // The Spectrum SDK surfaces Photon cloud CatchUpEvents failures as an + // iMessage internal error. Local Hermes allowlists cannot cause or fix this: + // inbound messages stop before they reach the gateway. Add an explicit hint + // so operators know to retry/restart or escalate to Photon support instead + // of chasing PHOTON_ALLOWED_USERS / pairing configuration. + const details = String(e?.cause?.details || e?.details || ""); + const path = String(e?.cause?.path || e?.path || ""); + const code = String(e?.code || ""); + if ( + path.includes("EventService/CatchUpEvents") || + details.includes("Unknown server error occurred") || + (code === "internalError" && msg.includes("Unknown server error")) + ) { + out += + " | Photon Spectrum CatchUpEvents returned an internal server error; " + + "this is upstream of Hermes, so inbound iMessages may not be delivered " + + "until Photon recovers or the stream is re-established."; + } + return out; +} + // spectrum-ts handles in-session gRPC reconnects internally, but if the async // iterator itself throws or ends, this consumer would stop forever. Wrap it in // a re-subscribe loop with capped exponential backoff + jitter so inbound @@ -365,10 +390,7 @@ async function normalizeEvent(space, message) { } console.error("photon-sidecar: inbound stream ended — re-subscribing"); } catch (e) { - console.error( - "photon-sidecar: inbound stream errored — restarting: " + - (e && e.message ? e.message : String(e)) - ); + console.error(inboundStreamErrorMessage(e)); } await new Promise((r) => setTimeout(r, backoff + Math.random() * backoff * 0.2) diff --git a/tests/plugins/platforms/photon/test_spectrum_patch.py b/tests/plugins/platforms/photon/test_spectrum_patch.py index 2f1943fa1190..15f3f85230d3 100644 --- a/tests/plugins/platforms/photon/test_spectrum_patch.py +++ b/tests/plugins/platforms/photon/test_spectrum_patch.py @@ -17,6 +17,15 @@ def test_sidecar_applies_spectrum_patch_before_importing_sdk() -> None: assert index.index("patchSpectrumTs();") < index.index('await import("spectrum-ts")') +def test_sidecar_labels_catchup_internal_errors_as_upstream_photon() -> None: + """Photon cloud stream failures should not look like local auth problems.""" + index = Path("plugins/platforms/photon/sidecar/index.mjs").read_text(encoding="utf-8") + assert "function inboundStreamErrorMessage" in index + assert "EventService/CatchUpEvents" in index + assert "this is upstream of Hermes" in index + assert "PHOTON_ALLOWED_USERS" in index + + def test_spectrum_patch_preserves_text_when_single_attachment(tmp_path: Path) -> None: """The sidecar dependency patch must turn text+one attachment into group content.""" dist = tmp_path / "node_modules" / "spectrum-ts" / "dist"