can1357/oh-my-pi · error · AuthBrokerError

Auth broker stream ended unexpectedly

Error message

Auth broker stream ended unexpectedly

What it means

After the SSE snapshot stream yields at least one event and the event loop terminates while the caller's AbortSignal was NOT aborted, openSnapshotStream throws "Auth broker stream ended unexpectedly" (client.ts:261-268). The stream is designed to be long-lived; the server closing it (or the connection dropping) after startup is a protocol failure, not a normal completion, because the client was never signalled to stop. This distinguishes server-initiated termination from intentional caller cancellation.

Source

Thrown at packages/ai/src/auth-broker/client.ts:262

				});
			}
			const validated = snapshotStreamEventSchema(parsed);
			if (validated instanceof type.errors) {
				throw new AuthBrokerError("Auth broker stream event failed schema validation", {
					body: validated.summary,
				});
			}
			const event = validated as SnapshotStreamEvent;
			if (!sawFirstEvent) {
				sawFirstEvent = true;
				if (event.kind !== "snapshot") {
					throw new AuthBrokerError("Auth broker stream did not start with snapshot", { body: sse.data });
				}
			}
			yield event;
		}
		if (!opts.signal?.aborted) {
			throw new AuthBrokerError(
				sawFirstEvent
					? "Auth broker stream ended unexpectedly"
					: "Auth broker stream ended before initial snapshot",
				{ status: response.status },
			);
		}
	}

	/**
	 * Fetch aggregate broker usage with a timeout sized for serialized
	 * same-provider account probes.
	 */
	fetchUsage(options: { signal?: AbortSignal; maxAccountsPerProvider?: number } = {}): Promise<UsageResponse> {
		const requestedAccountCount = options.maxAccountsPerProvider;
		const accountCount =
			typeof requestedAccountCount === "number" && Number.isFinite(requestedAccountCount)
				? Math.max(1, Math.floor(requestedAccountCount))
				: 1;

View on GitHub (pinned to 9690622007)

Solutions

  1. Wrap the openSnapshotStream iteration in a reconnect loop with backoff — the client intentionally does not auto-reconnect, so the caller owns reconnection.
  2. Check broker and proxy idle/keepalive timeouts (e.g. nginx proxy_read_timeout, cloud LB idle timeout) and raise them above your keepalive interval.
  3. Verify the broker didn't crash: check broker logs for panics/OOM around the disconnect time.
  4. Ensure the caller's signal isn't aborted by another component (an abort here would silently end the loop instead of throwing).

Example fix

// before
for await (const event of client.openSnapshotStream({ signal })) {
  applyEvent(event);
}

// after
while (!signal.aborted) {
  try {
    for await (const event of client.openSnapshotStream({ signal })) {
      applyEvent(event);
    }
    if (!signal.aborted) logger.warn("snapshot stream ended, reconnecting");
  } catch (err) {
    if (signal.aborted) break;
    logger.warn("snapshot stream error, retrying", { error: err });
  }
  await Bun.sleep(backoffMs); // exponential backoff
}
Defensive patterns

Strategy: retry

Type guard

function isStreamEndedUnexpectedly(err: unknown): err is AuthBrokerError {
  return err instanceof AuthBrokerError && err.message === "Auth broker stream ended unexpectedly";
}

Try / catch

while (!signal.aborted) {
  try {
    for await (const event of client.openSnapshotStream({ signal })) {
      applyEvent(event);
    }
  } catch (err) {
    if (signal.aborted) break;
    logger.warn("snapshot stream ended; reconnecting", { error: err });
  }
  await Bun.sleep(Math.min(baseMs * 2 ** attempt++, maxMs));
}

Prevention

When it happens

Trigger: Iterating openSnapshotStream() where: the first event was received, then the broker closes the connection or the TCP/HTTP2 connection drops, and opts.signal is not aborted. Emitted from the generator after readSseEvents exhausts response.body.

Common situations: Broker process restart or crash mid-stream; idle connection reaped by an intermediate NAT, load balancer, or reverse proxy (e.g. nginx proxy_read_timeout); network interruption on a laptop switching Wi-Fi; broker deploy rolling out a new version while clients hold open streams.

Related errors


AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31). Data as JSON: /api/errors/8edcf829d704bf2b. Report an issue: GitHub.