@@ -6,15 +6,17 @@ import {
66 extractRequestMetadata ,
77 insertMessageToBigQuery ,
88} from './helpers'
9- import { OpenRouterStreamChatCompletionChunkSchema } from './type/openrouter'
9+ import {
10+ OpenRouterErrorResponseSchema ,
11+ OpenRouterStreamChatCompletionChunkSchema ,
12+ } from './type/openrouter'
1013
1114import type { UsageData } from './helpers'
1215import type { OpenRouterStreamChatCompletionChunk } from './type/openrouter'
1316import type { InsertMessageBigqueryFn } from '@codebuff/common/types/contracts/bigquery'
1417import type { Logger } from '@codebuff/common/types/contracts/logger'
1518
1619type StreamState = { responseText : string ; reasoningText : string }
17-
1820function createOpenRouterRequest ( params : {
1921 body : any
2022 openrouterApiKey : string | null
@@ -93,9 +95,9 @@ export async function handleOpenRouterNonStream({
9395
9496 const responses = await Promise . all ( requests )
9597 if ( responses . every ( ( r ) => ! r . ok ) ) {
96- throw new Error (
97- `Failed to make all ${ n } requests: ${ responses . map ( ( r ) => r . statusText ) . join ( ', ' ) } ` ,
98- )
98+ // Return provider-specific error from the first failed response
99+ const firstFailedResponse = responses [ 0 ]
100+ throw await parseOpenRouterError ( firstFailedResponse )
99101 }
100102 const allData = await Promise . all ( responses . map ( ( r ) => r . json ( ) ) )
101103
@@ -183,9 +185,7 @@ export async function handleOpenRouterNonStream({
183185 } )
184186
185187 if ( ! response . ok ) {
186- throw new Error (
187- `OpenRouter API error (${ response . statusText } ): ${ await response . text ( ) } ` ,
188- )
188+ throw await parseOpenRouterError ( response )
189189 }
190190
191191 const data = await response . json ( )
@@ -261,9 +261,7 @@ export async function handleOpenRouterStream({
261261 } )
262262
263263 if ( ! response . ok ) {
264- throw new Error (
265- `OpenRouter API error (${ response . statusText } ): ${ await response . text ( ) } ` ,
266- )
264+ throw await parseOpenRouterError ( response )
267265 }
268266
269267 const reader = response . body ?. getReader ( )
@@ -532,3 +530,84 @@ async function handleStreamChunk({
532530 state . reasoningText += choice . delta ?. reasoning ?? ''
533531 return state
534532}
533+
534+ /**
535+ * Custom error class for OpenRouter API errors that preserves provider-specific details.
536+ */
537+ export class OpenRouterError extends Error {
538+ constructor (
539+ public readonly statusCode : number ,
540+ public readonly statusText : string ,
541+ public readonly errorBody : {
542+ error : {
543+ message : string
544+ code : string | number | null
545+ type ?: string | null
546+ param ?: unknown
547+ metadata ?: {
548+ raw ?: string
549+ provider_name ?: string
550+ }
551+ }
552+ } ,
553+ ) {
554+ super ( errorBody . error . message )
555+ this . name = 'OpenRouterError'
556+ }
557+
558+ /**
559+ * Returns the error in a format suitable for API responses.
560+ */
561+ toJSON ( ) {
562+ return {
563+ error : {
564+ message : this . errorBody . error . message ,
565+ code : this . errorBody . error . code ,
566+ type : this . errorBody . error . type ,
567+ param : this . errorBody . error . param ,
568+ metadata : this . errorBody . error . metadata ,
569+ } ,
570+ }
571+ }
572+ }
573+
574+ /**
575+ * Parses an error response from OpenRouter and returns an OpenRouterError.
576+ */
577+ async function parseOpenRouterError (
578+ response : Response ,
579+ ) : Promise < OpenRouterError > {
580+ const errorText = await response . text ( )
581+ let errorBody : OpenRouterError [ 'errorBody' ]
582+ try {
583+ const parsed = JSON . parse ( errorText )
584+ const validated = OpenRouterErrorResponseSchema . safeParse ( parsed )
585+ if ( validated . success ) {
586+ errorBody = {
587+ error : {
588+ message : validated . data . error . message ,
589+ code : validated . data . error . code ?? null ,
590+ type : validated . data . error . type ,
591+ param : validated . data . error . param ,
592+ // metadata is not in the schema but OpenRouter includes it for provider errors
593+ metadata : ( parsed as any ) . error ?. metadata ,
594+ } ,
595+ }
596+ } else {
597+ errorBody = {
598+ error : {
599+ message : errorText || response . statusText ,
600+ code : response . status ,
601+ } ,
602+ }
603+ }
604+ } catch {
605+ errorBody = {
606+ error : {
607+ message : errorText || response . statusText ,
608+ code : response . status ,
609+ } ,
610+ }
611+ }
612+ return new OpenRouterError ( response . status , response . statusText , errorBody )
613+ }
0 commit comments