Compare commits

..

1 Commits

Author SHA1 Message Date
diegosouzapw
9ea614ab12 fix(images): normalize image endpoint error format (#9981) 2026-08-10 10:32:34 -03:00
7 changed files with 77 additions and 15 deletions

View File

@@ -1 +0,0 @@
- fix(search): nest Exa contents options (text/highlights) for /search API (#9914)

View File

@@ -0,0 +1 @@
- fix(images): normalize terminal upstream errors via OpenAI-standard type/code (#9981)

View File

@@ -336,10 +336,8 @@ function buildExaRequest(
query: params.query,
numResults: params.maxResults,
type: "auto",
contents: {
text: true,
highlights: true,
},
text: true,
highlights: true,
};
if (includes.length) body.includeDomains = includes;
if (excludes.length) body.excludeDomains = excludes;

View File

@@ -308,10 +308,11 @@ async function postHandler(request, context) {
}
const errorPayload = toJsonErrorPayload((result as any).error, "Image generation provider error");
return new Response(JSON.stringify(errorPayload), {
status: (result as any).status,
headers: { "Content-Type": "application/json" },
});
const message =
typeof errorPayload?.error?.message === "string"
? errorPayload.error.message
: "Image generation provider error";
return errorResponse((result as any).status, message);
}
export const POST = withInjectionGuard(postHandler);

View File

@@ -119,8 +119,9 @@ export async function POST(request, { params }) {
}
const errorPayload = toJsonErrorPayload((result as any).error, "Image generation provider error");
return new Response(JSON.stringify(errorPayload), {
status: (result as any).status,
headers: { "Content-Type": "application/json" },
});
const message =
typeof errorPayload?.error?.message === "string"
? errorPayload.error.message
: "Image generation provider error";
return errorResponse((result as any).status, message);
}

View File

@@ -701,6 +701,67 @@ test("provider-scoped image generation POST uses the shared 401 account fallback
]);
});
test("v1 image generation POST normalizes a terminal upstream 401 to the OpenAI-standard error shape", async () => {
await seedConnection("openai", { apiKey: "single-expired-image-key" });
globalThis.fetch = async (url, options: RequestInit = {}) => {
assert.equal(String(url), "https://api.openai.com/v1/images/generations");
const authorization = new Headers(options.headers).get("authorization") ?? "";
assert.equal(authorization, "Bearer single-expired-image-key");
return new Response(JSON.stringify({ error: { message: "expired access token" } }), {
status: 401,
headers: { "content-type": "application/json" },
});
};
const response = await imageRoute.POST(
new Request("http://localhost/api/v1/images/generations", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ model: "openai/gpt-image-2", prompt: "normalize terminal 401" }),
})
);
const body = (await response.json()) as ErrorResponseBody;
assert.equal(response.status, 401);
assert.deepEqual(body.error, {
message: "expired access token",
type: "authentication_error",
code: "invalid_api_key",
});
});
test("provider-scoped image generation POST normalizes a terminal upstream 401 to the OpenAI-standard error shape", async () => {
await seedConnection("openai", { apiKey: "provider-single-expired-key" });
globalThis.fetch = async (url, options: RequestInit = {}) => {
assert.equal(String(url), "https://api.openai.com/v1/images/generations");
const authorization = new Headers(options.headers).get("authorization") ?? "";
assert.equal(authorization, "Bearer provider-single-expired-key");
return new Response(JSON.stringify({ error: { message: "expired provider token" } }), {
status: 401,
headers: { "content-type": "application/json" },
});
};
const response = await providerImageRoute.POST(
new Request("http://localhost/api/v1/providers/openai/images/generations", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ model: "gpt-image-2", prompt: "normalize provider terminal 401" }),
}),
{ params: Promise.resolve({ provider: "openai" }) }
);
const body = (await response.json()) as ErrorResponseBody;
assert.equal(response.status, 401);
assert.deepEqual(body.error, {
message: "expired provider token",
type: "authentication_error",
code: "invalid_api_key",
});
});
test("v1 image generation POST refreshes an expired Antigravity token before dispatch", async () => {
await seedConnection("antigravity", {
authType: "oauth",

View File

@@ -123,7 +123,7 @@ test("handleSearch builds Brave news requests and normalizes favicon metadata",
}
});
test("handleSearch builds Exa requests with contents-nested options, include/exclude domains, and preserves rich result fields", async () => {
test("handleSearch builds Exa requests with include/exclude domains and preserves rich result fields", async () => {
const originalFetch = globalThis.fetch;
let captured;
@@ -165,7 +165,8 @@ test("handleSearch builds Exa requests with contents-nested options, include/exc
query: "agentic workflows",
numResults: 5,
type: "auto",
contents: { text: true, highlights: true },
text: true,
highlights: true,
includeDomains: ["allowed.com"],
excludeDomains: ["blocked.com"],
category: "news",