@@ -13,6 +13,18 @@ export interface Rpc {
13
13
request ( service : string , method : string , data : JsonValue , headers ?: any ) : Promise < string > ;
14
14
}
15
15
16
+ export class TwirpError extends Error {
17
+ status : number ;
18
+ code ?: string ;
19
+
20
+ constructor ( name : string , message : string , status : number , code ?: string ) {
21
+ super ( message ) ;
22
+ this . name = name ;
23
+ this . status = status ;
24
+ this . code = code ;
25
+ }
26
+ }
27
+
16
28
/**
17
29
* JSON based Twirp V7 RPC
18
30
*/
@@ -47,9 +59,30 @@ export class TwirpRpc {
47
59
} ) ;
48
60
49
61
if ( ! response . ok ) {
50
- throw new Error ( `Request failed with status ${ response . status } : ${ response . statusText } ` ) ;
62
+ const isJson = response . headers . get ( 'content-type' ) === 'application/json' ;
63
+ let errorMessage = 'Unknown internal error' ;
64
+ let errorCode : string | undefined = undefined ;
65
+ try {
66
+ if ( isJson ) {
67
+ const parsedError = ( await response . json ( ) ) as Record < string , unknown > ;
68
+ if ( 'msg' in parsedError ) {
69
+ errorMessage = < string > parsedError . msg ;
70
+ }
71
+ if ( 'code' in parsedError ) {
72
+ errorCode = < string > parsedError . code ;
73
+ }
74
+ } else {
75
+ errorMessage = await response . text ( ) ;
76
+ }
77
+ } catch ( e ) {
78
+ // parsing went wrong, no op and we keep default error message
79
+ console . debug ( `Error when trying to parse error message, using defaults` , e ) ;
80
+ }
81
+
82
+ throw new TwirpError ( response . statusText , errorMessage , response . status , errorCode ) ;
51
83
}
52
84
const parsedResp = ( await response . json ( ) ) as Record < string , unknown > ;
85
+
53
86
const camelcaseKeys = await import ( 'camelcase-keys' ) . then ( ( mod ) => mod . default ) ;
54
87
return camelcaseKeys ( parsedResp , { deep : true } ) ;
55
88
}
0 commit comments