{"record":{"id":"1234de94f4e0f142","repo":"can1357/oh-my-pi","slug":"auth-broker-response-failed-schema-validation","errorCode":null,"errorMessage":"Auth broker response failed schema validation","messagePattern":"Auth broker response failed schema validation","errorType":"validation","errorClass":"AuthBrokerError","httpStatus":null,"severity":"error","filePath":"packages/ai/src/auth-broker/client.ts","lineNumber":179,"sourceCode":"\t\t};\n\t\tif (opts.ifGenerationGt !== undefined) headers[\"If-None-Match\"] = `\"${opts.ifGenerationGt}\"`;\n\t\tconst timeoutMs =\n\t\t\topts.waitMs !== undefined && opts.waitMs > 0 ? Math.max(this.#timeoutMs, opts.waitMs + 1000) : undefined;\n\t\tconst response = await this.#fetchRaw(\"GET\", path, {\n\t\t\tauth: true,\n\t\t\theaders,\n\t\t\tsignal: opts.signal,\n\t\t\ttimeoutMs,\n\t\t});\n\t\tconst etagGeneration = parseGenerationTag(response.headers.get(\"etag\"));\n\t\tif (response.status === 304) {\n\t\t\treturn { status: 304, generation: etagGeneration ?? opts.ifGenerationGt ?? 0 };\n\t\t}\n\t\tconst text = await response.text();\n\t\tconst raw = this.#parseJson(text, response.status);\n\t\tconst validated = snapshotResponseSchema(raw);\n\t\tif (validated instanceof type.errors) {\n\t\t\tthrow new AuthBrokerError(\"Auth broker response failed schema validation\", {\n\t\t\t\tstatus: response.status,\n\t\t\t\tbody: validated.summary,\n\t\t\t});\n\t\t}\n\t\tconst snapshot = validated as SnapshotResponse;\n\t\treturn { status: 200, snapshot, generation: etagGeneration ?? snapshot.generation };\n\t}\n\n\t/**\n\t * Subscribe to the broker's SSE snapshot stream. The first frame is always\n\t * a full `snapshot`; subsequent frames are `entry` upserts / refreshes or\n\t * `removed` deletes. Caller controls lifecycle via `opts.signal`.\n\t *\n\t * Throws {@link AuthBrokerStreamUnsupportedError} when the broker responds\n\t * 404 — older brokers predate this endpoint and the caller should fall back\n\t * to long-polling for the remainder of its lifetime.\n\t */\n\tasync *openSnapshotStream(opts: { signal?: AbortSignal } = {}): AsyncGenerator<SnapshotStreamEvent> {","sourceCodeStart":161,"sourceCodeEnd":197,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/ai/src/auth-broker/client.ts#L161-L197","documentation":"The auth broker client fetched a credential snapshot and parses the body as JSON, then validates it against snapshotResponseSchema (ArkType). When the payload does not match the schema, it throws AuthBrokerError with the HTTP status and a schema-error summary in the body, indicating the broker returned an unexpected or incompatible response shape.","triggerScenarios":"GET snapshot returned 200 with a body that fails snapshotResponseSchema: missing required fields, wrong types, extra restructuring from an incompatible broker version, or an intermediary (proxy/CAPTCHA page) returning non-broker JSON.","commonSituations":"Auth broker server deployed at a different (older/newer) API version than the client expects; corporate proxy or error page injecting HTML/JSON; misconfigured AUTH_BROKER_URL pointing at the wrong service.","solutions":["Verify AUTH_BROKER_URL points at a compatible auth broker version and redeploy/upgrade whichever side is stale","Inspect the error's body summary to see exactly which fields failed validation","Check for proxies or gateways rewriting the response body","Retry after transient broker deploy completes; if persistent, report/fix the broker's response contract"],"exampleFix":"// before: pointing client at wrong service\nAUTH_BROKER_URL=https://old-broker.internal\n// after: matching broker version\nAUTH_BROKER_URL=https://broker.internal/v2","handlingStrategy":"validation","validationCode":"const parsed = JSON.parse(text);\nconst checked = snapshotResponseSchema(parsed);\nif (checked instanceof type.errors) {\n  logger.warn(\"Snapshot shape mismatch — broker version drift?\", { summary: checked.summary });\n}","typeGuard":"function isValidSnapshot(raw: unknown): raw is SnapshotResponse {\n  return !(snapshotResponseSchema(raw) instanceof type.errors);\n}","tryCatchPattern":"try {\n  const snap = await client.fetchSnapshot();\n} catch (err) {\n  if (err instanceof AuthBrokerError && err.message.includes(\"schema validation\")) {\n    logger.error(\"Auth broker returned incompatible snapshot\", { status: err.status, body: err.body });\n    // fail soft: keep cached credentials, alert on version drift\n  } else throw err;\n}","preventionTips":["Pin/upgrade broker and client together so schemas match","Alert on schema-validation errors as a version-drift signal","Ensure proxies don't rewrite broker response bodies","Log the schema summary to identify the drifting field quickly"],"tags":["network","schema-validation","auth-broker"],"backgroundTag":"schema-validation-failed","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}