From d2a646410148ca456eb8e4fad29ac98a79e195bb Mon Sep 17 00:00:00 2001 From: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com> Date: Fri, 11 Sep 2026 18:18:50 -0300 Subject: [PATCH] fix(sse): stop null-prototype sanitized errors from crashing image call logs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit sanitizeUpstreamDetails() builds every object with Object.create(null) on purpose (#12506) so a hostile upstream key such as `__proto__` can never reach a real prototype. saveImageErrorResult() then handed that object to a bare String(), which throws "TypeError: Cannot convert object to primitive value" — so every Codex image failure came out as an unhandled crash instead of the sanitized error the caller was supposed to get. The null prototype is the correct behavior at the source, so the sink is what has to be total: `error` is typed `unknown`, and it now serializes objects structurally (the same way the Antigravity branch already logs its sanitized payload) and keeps String() semantics for everything else. The sanitized payload returned to the caller is untouched, so the redaction guarantees are unchanged. Covered by the three existing regression tests in tests/unit/image-generation-handler.test.ts (codex sanitizes upstream HTTP errors / model-access 400 retryable / ordinary 400 not retryable). --- open-sse/handlers/imageGeneration.ts | 36 +++++++++++++++++++++++++++- 1 file changed, 35 insertions(+), 1 deletion(-) diff --git a/open-sse/handlers/imageGeneration.ts b/open-sse/handlers/imageGeneration.ts index 62a9488565..8c3596e14d 100644 --- a/open-sse/handlers/imageGeneration.ts +++ b/open-sse/handlers/imageGeneration.ts @@ -2778,6 +2778,40 @@ export function saveImageSuccessResult({ }; } +/** + * Render an arbitrary `error` value as a call-log string. + * + * `saveImageErrorResult` takes `error: unknown`, and the Codex fan-out forwards + * whatever `sanitizeImageProviderError()` produced — i.e. the output of + * `sanitizeUpstreamDetails()`, which builds every object with + * `Object.create(null)` on purpose (#12506) so a hostile upstream key such as + * `__proto__` or `constructor` can never reach a real prototype. That object + * therefore has NO `toString`/`Symbol.toPrimitive`, so a bare `String(value)` + * throws `TypeError: Cannot convert object to primitive value` and turned every + * Codex image failure into an unhandled crash instead of the sanitized error. + * The null prototype is the correct behavior at the source, so the sink is what + * has to be total: serialize objects structurally (the same way the Antigravity + * branch already logs its sanitized payload) and keep `String()` semantics for + * everything else. + */ +function stringifyImageErrorForLog(value: unknown): string { + if (typeof value === "string") return value; + if (value instanceof Error) return `${value.name}: ${value.message}`; + if (value !== null && typeof value === "object") { + try { + const serialized = JSON.stringify(value); + if (typeof serialized === "string") return serialized; + } catch { + // Circular graph or a throwing toJSON — fall through to String(). + } + } + try { + return String(value); + } catch { + return "[unserializable error]"; + } +} + export function saveImageErrorResult({ provider, model, @@ -2810,7 +2844,7 @@ export function saveImageErrorResult({ model: `${provider}/${model}`, provider, duration: Date.now() - startTime, - error: typeof error === "string" ? error.slice(0, 500) : String(error).slice(0, 500), + error: stringifyImageErrorForLog(error).slice(0, 500), requestBody, }).catch(() => {});