From 0952acbf4de8b20517a88183fa649e8f793468ac Mon Sep 17 00:00:00 2001 From: XU SUN Date: Sun, 21 Jun 2026 10:49:39 +0200 Subject: [PATCH] fix(photon): label upstream CatchUpEvents failures --- plugins/platforms/photon/sidecar/index.mjs | 30 ++++++++++++++++--- .../platforms/photon/test_spectrum_patch.py | 9 ++++++ 2 files changed, 35 insertions(+), 4 deletions(-) diff --git a/plugins/platforms/photon/sidecar/index.mjs b/plugins/platforms/photon/sidecar/index.mjs index 148e9d377eb..5b5d20ed826 100644 --- a/plugins/platforms/photon/sidecar/index.mjs +++ b/plugins/platforms/photon/sidecar/index.mjs @@ -446,6 +446,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 @@ -471,10 +496,7 @@ async function normalizeEvent(space, message) { markStreamRecovering("inbound stream ended"); } catch (e) { const reason = e && e.message ? e.message : String(e); - console.error( - "photon-sidecar: inbound stream errored — restarting: " + - reason - ); + console.error(inboundStreamErrorMessage(e)); markStreamRecovering(reason); } await new Promise((r) => diff --git a/tests/plugins/platforms/photon/test_spectrum_patch.py b/tests/plugins/platforms/photon/test_spectrum_patch.py index a16774e1ffb..548466ed77a 100644 --- a/tests/plugins/platforms/photon/test_spectrum_patch.py +++ b/tests/plugins/platforms/photon/test_spectrum_patch.py @@ -26,6 +26,15 @@ def test_sidecar_healthz_reports_stream_health() -> None: assert "process.exit(75);" in index +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"