feat(sse): selective upstream 4xx error passthrough util

This commit is contained in:
diegosouzapw
2026-07-25 18:27:58 -03:00
parent 4053e2314a
commit 043756bc02
2 changed files with 84 additions and 0 deletions

View File

@@ -0,0 +1,38 @@
/**
* Selective upstream 4xx error passthrough (Claude Code auto-recover contract).
*
* Claude Code matches the upstream error WORDING to auto-disable capabilities
* (thinking / output_config) for the rest of the conversation. Wrapping the body
* via buildErrorBody() truncates the message and breaks that recovery. For
* upstream-originated 4xx errors the body is the provider's public API message —
* not our internals — so it is safe and required to relay it verbatim.
* OmniRoute-generated errors MUST keep using buildErrorBody() (Hard Rule #12).
*/
const PASSTHROUGH_MIN = 400;
const PASSTHROUGH_MAX = 499;
// 401/403/407: auth-adjacent — our own credential context may leak via provider
// echoes; keep those sanitized. 400/404/408/413/422/429 carry the capability and
// quota wording the client needs.
const EXCLUDED_STATUSES = new Set([401, 403, 407]);
const INTERNAL_LEAK_RE = /\sat\s\/|node_modules|omniroute\//i;
export function shouldPassthroughUpstreamError(statusCode: number, upstreamBody: unknown): boolean {
if (statusCode < PASSTHROUGH_MIN || statusCode > PASSTHROUGH_MAX) return false;
if (EXCLUDED_STATUSES.has(statusCode)) return false;
if (!upstreamBody || typeof upstreamBody !== "object") return false;
const text = JSON.stringify(upstreamBody);
if (INTERNAL_LEAK_RE.test(text)) return false;
return true;
}
export function buildPassthroughErrorResponse(
statusCode: number,
upstreamBody: unknown,
headers?: Record<string, string>
): Response | null {
if (!shouldPassthroughUpstreamError(statusCode, upstreamBody)) return null;
return new Response(JSON.stringify(upstreamBody), {
status: statusCode,
headers: { "Content-Type": "application/json", ...(headers || {}) },
});
}

View File

@@ -0,0 +1,46 @@
import test from "node:test";
import assert from "node:assert/strict";
import {
shouldPassthroughUpstreamError,
buildPassthroughErrorResponse,
} from "../../open-sse/utils/upstreamErrorPassthrough.ts";
test("upstream error passthrough", async (t) => {
await t.test("4xx com corpo JSON de erro do provider é elegível", () => {
const body = {
type: "error",
error: { type: "invalid_request_error", message: "thinking.type: adaptive is not supported" },
};
assert.equal(shouldPassthroughUpstreamError(400, body), true);
});
await t.test("5xx NÃO é elegível (segue sanitizado)", () => {
assert.equal(shouldPassthroughUpstreamError(500, { error: { message: "x" } }), false);
});
await t.test("corpo com cara de vazamento interno (stack trace) NÃO é elegível", () => {
assert.equal(
shouldPassthroughUpstreamError(400, {
error: { message: "Error\n at /usr/lib/node_modules/omniroute/x.js:1" },
}),
false
);
});
await t.test(
"401/407 NÃO são elegíveis (credencial nossa pode vazar em www-authenticate)",
() => {
assert.equal(shouldPassthroughUpstreamError(401, { error: { message: "bad key" } }), false);
}
);
await t.test("buildPassthroughErrorResponse preserva corpo byte-a-byte", async () => {
const body = {
type: "error",
error: { type: "invalid_request_error", message: "thinking.type: nope" },
};
const res = buildPassthroughErrorResponse(400, body);
assert.ok(res);
assert.equal(res.status, 400);
assert.deepEqual(await res.json(), body);
});
await t.test("retorna null quando inelegível", () => {
assert.equal(buildPassthroughErrorResponse(500, {}), null);
});
});