@@ -42,6 +42,13 @@ type OpenRouterChatCompletionParams = OpenAI.Chat.ChatCompletionCreateParams & {
4242 reasoning ?: OpenRouterReasoningParams
4343}
4444
45+ // OpenRouter error structure that may include metadata.raw with actual upstream error
46+ interface OpenRouterErrorResponse {
47+ message ?: string
48+ code ?: number
49+ metadata ?: { raw ?: string }
50+ }
51+
4552// See `OpenAI.Chat.Completions.ChatCompletionChunk["usage"]`
4653// `CompletionsAPI.CompletionUsage`
4754// See also: https://openrouter.ai/docs/use-cases/usage-accounting
@@ -109,6 +116,29 @@ export class OpenRouterHandler extends BaseProvider implements SingleCompletionH
109116 return this . currentReasoningDetails . length > 0 ? this . currentReasoningDetails : undefined
110117 }
111118
119+ /**
120+ * Handle OpenRouter streaming error response and report to telemetry.
121+ * OpenRouter may include metadata.raw with the actual upstream provider error.
122+ */
123+ private handleStreamingError ( error : OpenRouterErrorResponse , modelId : string , operation : string ) : never {
124+ const rawErrorMessage = error ?. metadata ?. raw || error ?. message
125+
126+ const apiError = Object . assign (
127+ new ApiProviderError (
128+ rawErrorMessage ?? "Unknown error" ,
129+ this . providerName ,
130+ modelId ,
131+ operation ,
132+ error ?. code ,
133+ ) ,
134+ { status : error ?. code , error : { message : error ?. message , metadata : error ?. metadata } } ,
135+ )
136+
137+ TelemetryService . instance . captureException ( apiError )
138+
139+ throw new Error ( `OpenRouter API Error ${ error ?. code } : ${ rawErrorMessage } ` )
140+ }
141+
112142 override async * createMessage (
113143 systemPrompt : string ,
114144 messages : Anthropic . Messages . MessageParam [ ] ,
@@ -226,15 +256,9 @@ export class OpenRouterHandler extends BaseProvider implements SingleCompletionH
226256 try {
227257 stream = await this . client . chat . completions . create ( completionParams , requestOptions )
228258 } catch ( error ) {
229- TelemetryService . instance . captureException (
230- new ApiProviderError (
231- error instanceof Error ? error . message : String ( error ) ,
232- this . providerName ,
233- modelId ,
234- "createMessage" ,
235- ) ,
236- )
237-
259+ const errorMessage = error instanceof Error ? error . message : String ( error )
260+ const apiError = new ApiProviderError ( errorMessage , this . providerName , modelId , "createMessage" )
261+ TelemetryService . instance . captureException ( apiError )
238262 throw handleOpenAIError ( error , this . providerName )
239263 }
240264
@@ -257,23 +281,7 @@ export class OpenRouterHandler extends BaseProvider implements SingleCompletionH
257281 for await ( const chunk of stream ) {
258282 // OpenRouter returns an error object instead of the OpenAI SDK throwing an error.
259283 if ( "error" in chunk ) {
260- const error = chunk . error as { message ?: string ; code ?: number }
261- console . error ( `OpenRouter API Error: ${ error ?. code } - ${ error ?. message } ` )
262-
263- TelemetryService . instance . captureException (
264- Object . assign (
265- new ApiProviderError (
266- error ?. message ?? "Unknown error" ,
267- this . providerName ,
268- modelId ,
269- "createMessage" ,
270- error ?. code ,
271- ) ,
272- { status : error ?. code } ,
273- ) ,
274- )
275-
276- throw new Error ( `OpenRouter API Error ${ error ?. code } : ${ error ?. message } ` )
284+ this . handleStreamingError ( chunk . error as OpenRouterErrorResponse , modelId , "createMessage" )
277285 }
278286
279287 const delta = chunk . choices [ 0 ] ?. delta
@@ -468,36 +476,14 @@ export class OpenRouterHandler extends BaseProvider implements SingleCompletionH
468476 try {
469477 response = await this . client . chat . completions . create ( completionParams , requestOptions )
470478 } catch ( error ) {
471- TelemetryService . instance . captureException (
472- new ApiProviderError (
473- error instanceof Error ? error . message : String ( error ) ,
474- this . providerName ,
475- modelId ,
476- "completePrompt" ,
477- ) ,
478- )
479-
479+ const errorMessage = error instanceof Error ? error . message : String ( error )
480+ const apiError = new ApiProviderError ( errorMessage , this . providerName , modelId , "completePrompt" )
481+ TelemetryService . instance . captureException ( apiError )
480482 throw handleOpenAIError ( error , this . providerName )
481483 }
482484
483485 if ( "error" in response ) {
484- const error = response . error as { message ?: string ; code ?: number }
485- console . error ( `OpenRouter API Error: ${ error ?. code } - ${ error ?. message } ` )
486-
487- TelemetryService . instance . captureException (
488- Object . assign (
489- new ApiProviderError (
490- error ?. message ?? "Unknown error" ,
491- this . providerName ,
492- modelId ,
493- "completePrompt" ,
494- error ?. code ,
495- ) ,
496- { status : error ?. code } ,
497- ) ,
498- )
499-
500- throw new Error ( `OpenRouter API Error ${ error ?. code } : ${ error ?. message } ` )
486+ this . handleStreamingError ( response . error as OpenRouterErrorResponse , modelId , "completePrompt" )
501487 }
502488
503489 const completion = response as OpenAI . Chat . ChatCompletion
0 commit comments