Skip to content

Commit e858654

Browse files
authored
fix (provider/gateway): image/video error handler (#12506)
# Add Gateway Timeout Error Handling Examples ## Background Proper error handling for timeout scenarios is crucial for applications using the AI Gateway. This PR improves the error handling for timeout situations and adds examples demonstrating how these errors are handled. ## Summary - Fixed error handling in Gateway models by making `asGatewayError` async in both image and video model implementations - Added three new examples demonstrating timeout error handling for: - Image generation (`generate-image/gateway-timeout.ts`) - Video generation (`generate-video/gateway-timeout.ts`) - Text streaming (`stream-text/gateway-timeout.ts`) Each example uses undici with an extremely short timeout (1ms) to deliberately trigger timeout errors, showing how the Gateway SDK catches and provides helpful error messages with troubleshooting guidance. ## Manual Verification Tested each example by running them with the AI Gateway API key set. Confirmed that they properly trigger timeout errors and display the expected error information including the original error cause. ## Checklist - [ ] Tests have been added / updated (for bug fixes / features) - [ ] Documentation has been added / updated (for bug fixes / features) - [ ] A _patch_ changeset for relevant packages has been added (for bug fixes / features - run `pnpm changeset` in the project root) - [ ] I have reviewed this pull request (self-review)
1 parent e5dc2ba commit e858654

File tree

6 files changed

+276
-2
lines changed

6 files changed

+276
-2
lines changed

.changeset/six-weeks-thank.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@ai-sdk/gateway': patch
3+
---
4+
5+
fix (provider/gateway): Fixed error handling in Gateway models by making asGatewayError async in both image and video model implementations.
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
/**
2+
* Example demonstrating Gateway timeout error handling for image generation
3+
*
4+
* This example uses undici with an extremely short timeout (1ms) to trigger
5+
* a timeout error. The Gateway SDK will catch this and provide a helpful
6+
* error message with troubleshooting guidance.
7+
*
8+
* Prerequisites:
9+
* - Set AI_GATEWAY_API_KEY environment variable
10+
* (See .env.example for setup instructions)
11+
*
12+
* Run: pnpm tsx src/generate-image/gateway-timeout.ts
13+
*/
14+
import { createGateway, generateImage } from 'ai';
15+
import { Agent, fetch as undiciFetch } from 'undici';
16+
import { run } from '../lib/run';
17+
18+
run(async () => {
19+
try {
20+
// Create an undici Agent with very short timeouts
21+
// bodyTimeout applies to receiving the entire response body
22+
const agent = new Agent({
23+
headersTimeout: 1, // 1ms - will timeout waiting for headers
24+
bodyTimeout: 1, // 1ms - will timeout reading response body
25+
});
26+
27+
// Create custom fetch using undici with the configured agent
28+
const customFetch = (
29+
url: string | URL | Request,
30+
options?: RequestInit,
31+
): Promise<Response> => {
32+
return undiciFetch(url as Parameters<typeof undiciFetch>[0], {
33+
...(options as any),
34+
dispatcher: agent,
35+
}) as Promise<Response>;
36+
};
37+
38+
// Create gateway provider with custom fetch
39+
const gateway = createGateway({
40+
fetch: customFetch,
41+
});
42+
43+
console.log('Making image generation request with 1ms timeout...');
44+
console.log(
45+
'This should timeout immediately and show the timeout error handling.\n',
46+
);
47+
48+
const { image } = await generateImage({
49+
model: gateway.imageModel('bfl/flux-2-pro'),
50+
prompt: 'A serene mountain landscape at sunset',
51+
});
52+
53+
console.log('Success! Image generated:');
54+
console.log('Base64 length:', image.base64.length);
55+
console.log('Media type:', image.mediaType);
56+
} catch (error) {
57+
console.error(
58+
'╔════════════════════════════════════════════════════════════════╗',
59+
);
60+
console.error(
61+
'║ TIMEOUT ERROR CAUGHT ║',
62+
);
63+
console.error(
64+
'╚════════════════════════════════════════════════════════════════╝\n',
65+
);
66+
console.error('Error Name:', (error as Error).name);
67+
console.error('Error Type:', (error as any).type);
68+
console.error('Status Code:', (error as any).statusCode);
69+
console.error('Error Code:', (error as any).code);
70+
console.error('\nError Message:');
71+
console.error('─'.repeat(70));
72+
console.error((error as Error).message);
73+
console.error('─'.repeat(70));
74+
75+
// Log the cause to see the original undici error
76+
if ((error as any).cause) {
77+
console.error('\n📋 Original Error (cause):');
78+
console.error(' Name:', ((error as any).cause as Error).name);
79+
console.error(' Code:', ((error as any).cause as any).code);
80+
console.error(' Message:', ((error as any).cause as Error).message);
81+
console.error(
82+
' Constructor:',
83+
((error as any).cause as Error).constructor.name,
84+
);
85+
}
86+
}
87+
});
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
/**
2+
* Example demonstrating Gateway timeout error handling for video generation
3+
*
4+
* This example uses undici with an extremely short timeout (1ms) to trigger
5+
* a timeout error. The Gateway SDK will catch this and provide a helpful
6+
* error message with troubleshooting guidance.
7+
*
8+
* Prerequisites:
9+
* - Set AI_GATEWAY_API_KEY environment variable
10+
* (See .env.example for setup instructions)
11+
*
12+
* Run: pnpm tsx src/generate-video/gateway-timeout.ts
13+
*/
14+
import { createGateway, experimental_generateVideo } from 'ai';
15+
import { Agent, fetch as undiciFetch } from 'undici';
16+
import { run } from '../lib/run';
17+
18+
run(async () => {
19+
try {
20+
// Create an undici Agent with very short timeouts
21+
// bodyTimeout applies to receiving the entire response body
22+
const agent = new Agent({
23+
headersTimeout: 1, // 1ms - will timeout waiting for headers
24+
bodyTimeout: 1, // 1ms - will timeout reading response body
25+
});
26+
27+
// Create custom fetch using undici with the configured agent
28+
const customFetch = (
29+
url: string | URL | Request,
30+
options?: RequestInit,
31+
): Promise<Response> => {
32+
return undiciFetch(url as Parameters<typeof undiciFetch>[0], {
33+
...(options as any),
34+
dispatcher: agent,
35+
}) as Promise<Response>;
36+
};
37+
38+
// Create gateway provider with custom fetch
39+
const gateway = createGateway({
40+
fetch: customFetch,
41+
});
42+
43+
console.log('Making video generation request with 1ms timeout...');
44+
console.log(
45+
'This should timeout immediately and show the timeout error handling.\n',
46+
);
47+
48+
const { videos } = await experimental_generateVideo({
49+
model: gateway.videoModel('google/veo-3.1-generate-001'),
50+
prompt: 'A resplendent quetzal flying through the rainforest',
51+
});
52+
53+
console.log('Success! Video generated:');
54+
console.log('Number of videos:', videos.length);
55+
if (videos[0]) {
56+
console.log('Base64 length:', videos[0].base64.length);
57+
console.log('Media type:', videos[0].mediaType);
58+
}
59+
} catch (error) {
60+
console.error(
61+
'╔════════════════════════════════════════════════════════════════╗',
62+
);
63+
console.error(
64+
'║ TIMEOUT ERROR CAUGHT ║',
65+
);
66+
console.error(
67+
'╚════════════════════════════════════════════════════════════════╝\n',
68+
);
69+
console.error('Error Name:', (error as Error).name);
70+
console.error('Error Type:', (error as any).type);
71+
console.error('Status Code:', (error as any).statusCode);
72+
console.error('Error Code:', (error as any).code);
73+
console.error('\nError Message:');
74+
console.error('─'.repeat(70));
75+
console.error((error as Error).message);
76+
console.error('─'.repeat(70));
77+
78+
// Log the cause to see the original undici error
79+
if ((error as any).cause) {
80+
console.error('\n📋 Original Error (cause):');
81+
console.error(' Name:', ((error as any).cause as Error).name);
82+
console.error(' Code:', ((error as any).cause as any).code);
83+
console.error(' Message:', ((error as any).cause as Error).message);
84+
console.error(
85+
' Constructor:',
86+
((error as any).cause as Error).constructor.name,
87+
);
88+
}
89+
}
90+
});
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
/**
2+
* Example demonstrating Gateway timeout error handling for streaming text
3+
*
4+
* This example uses undici with an extremely short timeout (1ms) to trigger
5+
* a timeout error. The Gateway SDK will catch this and provide a helpful
6+
* error message with troubleshooting guidance.
7+
*
8+
* Prerequisites:
9+
* - Set AI_GATEWAY_API_KEY environment variable
10+
* (See .env.example for setup instructions)
11+
*
12+
* Run: pnpm tsx src/stream-text/gateway-timeout.ts
13+
*/
14+
import { createGateway, streamText } from 'ai';
15+
import { Agent, fetch as undiciFetch } from 'undici';
16+
import { run } from '../lib/run';
17+
18+
run(async () => {
19+
try {
20+
// Create an undici Agent with extremely short timeouts
21+
// Using 1ms to ensure timeout before response arrives
22+
// For streaming, bodyTimeout will trigger while reading the stream
23+
const agent = new Agent({
24+
headersTimeout: 1, // 1ms - will timeout waiting for headers
25+
bodyTimeout: 1, // 1ms - will timeout reading response body
26+
});
27+
28+
// Create custom fetch using undici with the configured agent
29+
const customFetch = (
30+
url: string | URL | Request,
31+
options?: RequestInit,
32+
): Promise<Response> => {
33+
return undiciFetch(url as Parameters<typeof undiciFetch>[0], {
34+
...(options as any),
35+
dispatcher: agent,
36+
}) as Promise<Response>;
37+
};
38+
39+
// Create gateway provider with custom fetch
40+
const gateway = createGateway({
41+
fetch: customFetch,
42+
});
43+
44+
console.log('Making streaming request with 1ms timeout...');
45+
console.log(
46+
'This should timeout immediately and show the timeout error handling.\n',
47+
);
48+
49+
const result = streamText({
50+
model: gateway('anthropic/claude-3.5-sonnet'),
51+
prompt:
52+
'Write a detailed essay about the history of artificial intelligence, covering major milestones from the 1950s to present day.',
53+
});
54+
55+
console.log('Success! Streaming response:');
56+
for await (const chunk of result.textStream) {
57+
process.stdout.write(chunk);
58+
}
59+
console.log();
60+
console.log('\nUsage:', await result.usage);
61+
} catch (error) {
62+
console.error(
63+
'╔════════════════════════════════════════════════════════════════╗',
64+
);
65+
console.error(
66+
'║ TIMEOUT ERROR CAUGHT ║',
67+
);
68+
console.error(
69+
'╚════════════════════════════════════════════════════════════════╝\n',
70+
);
71+
console.error('Error Name:', (error as Error).name);
72+
console.error('Error Type:', (error as any).type);
73+
console.error('Status Code:', (error as any).statusCode);
74+
console.error('Error Code:', (error as any).code);
75+
console.error('\nError Message:');
76+
console.error('─'.repeat(70));
77+
console.error((error as Error).message);
78+
console.error('─'.repeat(70));
79+
80+
// Log the cause to see the original undici error
81+
if ((error as any).cause) {
82+
console.error('\n📋 Original Error (cause):');
83+
console.error(' Name:', ((error as any).cause as Error).name);
84+
console.error(' Code:', ((error as any).cause as any).code);
85+
console.error(' Message:', ((error as any).cause as Error).message);
86+
console.error(
87+
' Constructor:',
88+
((error as any).cause as Error).constructor.name,
89+
);
90+
}
91+
}
92+
});

packages/gateway/src/gateway-image-model.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@ export class GatewayImageModel implements ImageModelV3 {
104104
}),
105105
};
106106
} catch (error) {
107-
throw asGatewayError(error, await parseAuthMethod(resolvedHeaders));
107+
throw await asGatewayError(error, await parseAuthMethod(resolvedHeaders));
108108
}
109109
}
110110

packages/gateway/src/gateway-video-model.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,7 @@ export class GatewayVideoModel implements Experimental_VideoModelV3 {
107107
},
108108
};
109109
} catch (error) {
110-
throw asGatewayError(error, await parseAuthMethod(resolvedHeaders));
110+
throw await asGatewayError(error, await parseAuthMethod(resolvedHeaders));
111111
}
112112
}
113113

0 commit comments

Comments
 (0)