Compare commits

...

2 Commits

2 changed files with 29 additions and 1 deletions
@@ -558,5 +558,27 @@ describe('LobeCloudflareAI', () => {
expect(result).toHaveLength(2);
});
it('should return an empty list when Cloudflare returns a null result envelope', async () => {
// Arrange: Cloudflare's V4 envelope carries `result: null` on auth / account
// errors. Without the fallback, `result.map()` throws a 500 TypeError.
const apiKey = 'test_api_key';
const instance = new LobeCloudflareAI({ apiKey, baseURLOrAccountID: accountID });
vi.spyOn(globalThis, 'fetch').mockResolvedValue(
new Response(
JSON.stringify({
errors: [{ code: 10000, message: 'Authentication error' }],
messages: [],
result: null,
success: false,
}),
{ status: 400 },
),
);
// Act & Assert
await expect(instance.models()).resolves.toEqual([]);
});
});
});
@@ -162,7 +162,13 @@ export class LobeCloudflareAI implements LobeRuntimeAI {
});
const json = await response.json();
const modelList: CloudflareModelCard[] = json.result;
// Cloudflare's V4 envelope returns `result: null` (not an array) on auth /
// account errors, so fall back to an empty list to avoid a 500
// `Cannot read properties of null (reading 'map')`. The model-list fetch is
// best-effort and the client swallows failures, so a misconfigured key just
// yields "no models" rather than a surfaced error.
// https://developers.cloudflare.com/fundamentals/api/reference/responses/
const modelList: CloudflareModelCard[] = json?.result ?? [];
return modelList
.map((model) => {