From c4a81bf40f74095b2fea3c87aa5741043ac4faa7 Mon Sep 17 00:00:00 2001 From: Kevin Flansburg Date: Tue, 31 Mar 2026 16:14:26 -0600 Subject: [PATCH] Print a more sensible error when the user's token is expired --- src/cloudflare/internal/ai-api.ts | 8 +++++ .../internal/test/ai/ai-api-test.js | 33 +++++++++++++++++++ src/cloudflare/internal/test/ai/ai-mock.js | 12 +++++++ 3 files changed, 53 insertions(+) diff --git a/src/cloudflare/internal/ai-api.ts b/src/cloudflare/internal/ai-api.ts index 1ad1b3d7393..76263e3eb87 100644 --- a/src/cloudflare/internal/ai-api.ts +++ b/src/cloudflare/internal/ai-api.ts @@ -386,6 +386,14 @@ export class Ai { return new InferenceUpstreamError(content); } } catch { + // FL2 returns a plain text "error code: 1031" response when the edge + // preview token has expired (e.g. during `wrangler dev`). + if (content.includes('error code: 1031')) { + return new InferenceUpstreamError( + `${content} (your API token may have expired — try running \`wrangler login\` to obtain a fresh token and restart your dev server)` + ); + } + return new InferenceUpstreamError(content); } } diff --git a/src/cloudflare/internal/test/ai/ai-api-test.js b/src/cloudflare/internal/test/ai/ai-api-test.js index e9b31898c43..aaa84435303 100644 --- a/src/cloudflare/internal/test/ai/ai-api-test.js +++ b/src/cloudflare/internal/test/ai/ai-api-test.js @@ -88,6 +88,39 @@ export const tests = { assert.equal(err.message, 'Unknown error'); } + { + // Test FL2 plain text "error code: 1031" (expired preview token) + // includes wrangler login hint. FL2 returns this as text/plain when + // the edge preview token has expired during `wrangler dev`. + const err = await env.ai._parseError( + new Response('error code: 1031', { + status: 400, + headers: { 'content-type': 'text/plain; charset=UTF-8' }, + }) + ); + assert.equal(err.name, 'InferenceUpstreamError'); + assert.ok(err.message.includes('1031'), 'should include error code 1031'); + assert.ok( + err.message.includes('wrangler login'), + 'should suggest running wrangler login' + ); + } + + { + // Test error code 1031 via mock (expired preview token model) + try { + await env.ai.run('expiredTokenModel', { prompt: 'test' }); + assert.fail('should have thrown'); + } catch (e) { + assert.equal(e.name, 'InferenceUpstreamError'); + assert.ok(e.message.includes('1031'), 'should include error code 1031'); + assert.ok( + e.message.includes('wrangler login'), + 'should suggest running wrangler login' + ); + } + } + { // Test raw input const resp = await env.ai.run('rawInputs', { prompt: 'test' }); diff --git a/src/cloudflare/internal/test/ai/ai-mock.js b/src/cloudflare/internal/test/ai/ai-mock.js index 2b1746a985f..fe4d352d276 100644 --- a/src/cloudflare/internal/test/ai/ai-mock.js +++ b/src/cloudflare/internal/test/ai/ai-mock.js @@ -126,6 +126,18 @@ export default { ); } + if (modelName === 'expiredTokenModel') { + // Simulate the real FL2 response when an edge preview token has expired. + // FL2 returns plain text "error code: 1031" with HTTP 400. + return new Response('error code: 1031', { + status: 400, + headers: { + 'content-type': 'text/plain; charset=UTF-8', + ...respHeaders, + }, + }); + } + // Handle websocket requests if (isWebsocket && modelName === '@cf/test/websocket') { // For websocket requests, extract data from URL 'body' parameter