This commit is contained in:
Naveen 2026-01-30 23:54:14 +08:00 committed by GitHub
commit 8c1882b0f6
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 93 additions and 10 deletions

View File

@ -282,17 +282,23 @@ export async function fetchFirecrawlContent(params: {
storeInCache: params.storeInCache,
};
const res = await fetch(endpoint, {
method: "POST",
headers: {
Authorization: `Bearer ${params.apiKey}`,
"Content-Type": "application/json",
},
body: JSON.stringify(body),
signal: withTimeout(undefined, params.timeoutSeconds * 1000),
});
let res: Response;
try {
res = await fetch(endpoint, {
method: "POST",
headers: {
Authorization: `Bearer ${params.apiKey}`,
"Content-Type": "application/json",
},
body: JSON.stringify(body),
signal: withTimeout(undefined, params.timeoutSeconds * 1000),
});
} catch (err) {
const message = err instanceof Error ? err.message : String(err);
throw new Error(`Firecrawl fetch failed (network error): ${message}`);
}
const payload = (await res.json()) as {
let payload: {
success?: boolean;
data?: {
markdown?: string;
@ -306,6 +312,12 @@ export async function fetchFirecrawlContent(params: {
warning?: string;
error?: string;
};
try {
payload = (await res.json()) as typeof payload;
} catch (err) {
const message = err instanceof Error ? err.message : String(err);
throw new Error(`Firecrawl fetch failed (invalid response): ${message}`);
}
if (!res.ok || payload?.success === false) {
const detail = payload?.error || res.statusText;

View File

@ -269,4 +269,75 @@ describe("web_fetch extraction fallbacks", () => {
/Web fetch failed \(500\):.*Oops/,
);
});
it("handles network-level fetch failure in firecrawl fallback", async () => {
const mockFetch = vi.fn((input: RequestInfo) => {
const url = requestUrl(input);
if (url.includes("api.firecrawl.dev")) {
return Promise.reject(new TypeError("fetch failed"));
}
// Direct fetch fails with 403
return Promise.resolve({
ok: false,
status: 403,
headers: makeHeaders({ "content-type": "text/html" }),
text: async () => "blocked",
} as Response);
});
// @ts-expect-error mock fetch
global.fetch = mockFetch;
const tool = createWebFetchTool({
config: {
tools: {
web: {
fetch: { cacheTtlMinutes: 0, firecrawl: { apiKey: "firecrawl-test" } },
},
},
},
sandboxed: false,
});
await expect(
tool?.execute?.("call", { url: "https://example.com/network-error" }),
).rejects.toThrow(/Firecrawl fetch failed \(network error\)/);
});
it("handles invalid JSON response from firecrawl", async () => {
const mockFetch = vi.fn((input: RequestInfo) => {
const url = requestUrl(input);
if (url.includes("api.firecrawl.dev")) {
return Promise.resolve({
ok: true,
status: 200,
json: async () => {
throw new SyntaxError("Unexpected token");
},
} as Response);
}
return Promise.resolve({
ok: false,
status: 403,
headers: makeHeaders({ "content-type": "text/html" }),
text: async () => "blocked",
} as Response);
});
// @ts-expect-error mock fetch
global.fetch = mockFetch;
const tool = createWebFetchTool({
config: {
tools: {
web: {
fetch: { cacheTtlMinutes: 0, firecrawl: { apiKey: "firecrawl-test" } },
},
},
},
sandboxed: false,
});
await expect(
tool?.execute?.("call", { url: "https://example.com/invalid-json" }),
).rejects.toThrow(/Firecrawl fetch failed \(invalid response\)/);
});
});