can1357/oh-my-pi · error · AuthBrokerError

Auth broker returned malformed JSON

Error message

Auth broker returned malformed JSON

What it means

#parseJson attempts JSON.parse on every successful (2xx/304) response body before schema validation (client.ts:423-433). If the body is not valid JSON — including empty-string bodies (which are mapped to null and then typically fail schema validation downstream instead) — the client wraps the SyntaxError as AuthBrokerError "Auth broker returned malformed JSON" with the raw text as `body` and the parse error as `cause`. Note this applies to the request path; the SSE stream path has its own separate malformed-JSON error.

Source

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

	): Promise<t> {
		const response = await this.#fetchRaw(method, path, opts);
		const text = await response.text();
		const raw = this.#parseJson(text, response.status);
		const validated = RESPONSE_SCHEMAS[opts.schema](raw);
		if (validated instanceof type.errors) {
			throw new AuthBrokerError("Auth broker response failed schema validation", {
				status: response.status,
				body: validated.summary,
			});
		}
		return validated as t;
	}

	#parseJson(text: string, status: number): unknown {
		try {
			return text.length === 0 ? null : JSON.parse(text);
		} catch (parseError) {
			throw new AuthBrokerError("Auth broker returned malformed JSON", {
				status,
				body: text,
				cause: parseError,
			});
		}
	}

	async #fetchRaw(
		method: "GET" | "POST" | "DELETE",
		path: string,
		opts: {
			auth?: boolean;
			body?: unknown;
			signal?: AbortSignal;
			headers?: Record<string, string>;
			timeoutMs?: number;
		},
	): Promise<Response> {

View on GitHub (pinned to 9690622007)

Solutions

  1. Inspect err.body — it contains the raw text the server sent; if it's HTML you're being intercepted (proxy, captive portal, auth wall).
  2. Check err.cause for the JSON.parse position/message to identify truncation vs. wrong content type.
  3. Fix or bypass the intercepting proxy/network path; ensure the broker URL is directly reachable from the client.
  4. Verify the broker sets Content-Type: application/json and emits exactly one JSON document with no trailing data.
  5. Catch the error and fall back to fetchSnapshot()/retry if the broker is flaky under load.

Example fix

// before
const summary = await client.fetchClientUsageSummary();

// after
try {
  const summary = await client.fetchClientUsageSummary();
} catch (err) {
  if (err instanceof AuthBrokerError && err.message === "Auth broker returned malformed JSON") {
    logger.error("broker returned non-JSON body", { status: err.status, snippet: err.body?.slice(0, 200) });
    throw new Error("Auth broker response was not JSON — check proxy/network path");
  }
  throw err;
}
Defensive patterns

Strategy: try-catch

Type guard

function isMalformedJsonError(err: unknown): err is AuthBrokerError {
  return err instanceof AuthBrokerError && err.message === "Auth broker returned malformed JSON";
}

Try / catch

try {
  result = await client.healthz();
} catch (err) {
  if (isMalformedJsonError(err)) {
    logger.error("non-JSON broker response", { status: err.status, snippet: err.body?.slice(0, 200), cause: err.cause });
    throw new Error("Auth broker returned non-JSON — check for proxy/captive-portal interception");
  }
  throw err;
}

Prevention

When it happens

Trigger: Any #request-backed method (healthz, fetchUsage, fetchUsageHistory, reportClientUsage, fetchClientUsageSummary, notifyUsageStale, credential upload/block/disable/refresh/blocks-delete, listDisabledCredentials) returning 2xx with a body that JSON.parse cannot parse: HTML error pages, plain-text messages, truncated JSON, BOM-prefixed bodies, or NDJSON instead of a single JSON document.

Common situations: Reverse proxy or captive portal injecting an HTML interstitial/login page on a 200; misconfigured server returning plain text; response truncated by a proxy or connection reset mid-body; gzip/transfer-encoding mishandling producing garbage bytes; a custom broker writing NDJSON or trailing junk after the JSON value.

Understand the failure class

Related errors


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