@@ -1003,6 +1003,159 @@ describe("session.message-v2.fromError", () => {
10031003 } )
10041004 } )
10051005
1006+ test ( "retries 5xx Error with statusCode property" , ( ) => {
1007+ const err = Object . assign ( new Error ( "Internal Server Error" ) , { statusCode : 502 } )
1008+
1009+ const result = MessageV2 . fromError ( err , { providerID } )
1010+
1011+ expect ( MessageV2 . APIError . isInstance ( result ) ) . toBe ( true )
1012+ expect ( ( result as MessageV2 . APIError ) . data . isRetryable ) . toBe ( true )
1013+ expect ( ( result as MessageV2 . APIError ) . data . statusCode ) . toBe ( 502 )
1014+ } )
1015+
1016+ test ( "retries 5xx Error with status property" , ( ) => {
1017+ const err = Object . assign ( new Error ( "Bad Gateway" ) , { status : 503 } )
1018+
1019+ const result = MessageV2 . fromError ( err , { providerID } )
1020+
1021+ expect ( MessageV2 . APIError . isInstance ( result ) ) . toBe ( true )
1022+ expect ( ( result as MessageV2 . APIError ) . data . isRetryable ) . toBe ( true )
1023+ expect ( ( result as MessageV2 . APIError ) . data . statusCode ) . toBe ( 503 )
1024+ } )
1025+
1026+ test ( "retries 5xx Error with response.status" , ( ) => {
1027+ const err = Object . assign ( new Error ( "Gateway Timeout" ) , { response : { status : 504 } } )
1028+
1029+ const result = MessageV2 . fromError ( err , { providerID } )
1030+
1031+ expect ( MessageV2 . APIError . isInstance ( result ) ) . toBe ( true )
1032+ expect ( ( result as MessageV2 . APIError ) . data . isRetryable ) . toBe ( true )
1033+ expect ( ( result as MessageV2 . APIError ) . data . statusCode ) . toBe ( 504 )
1034+ } )
1035+
1036+ test ( "retries 5xx Error with response.statusCode" , ( ) => {
1037+ const err = Object . assign ( new Error ( "Bad Gateway" ) , { response : { statusCode : 502 } } )
1038+
1039+ const result = MessageV2 . fromError ( err , { providerID } )
1040+
1041+ expect ( MessageV2 . APIError . isInstance ( result ) ) . toBe ( true )
1042+ expect ( ( result as MessageV2 . APIError ) . data . isRetryable ) . toBe ( true )
1043+ expect ( ( result as MessageV2 . APIError ) . data . statusCode ) . toBe ( 502 )
1044+ } )
1045+
1046+ test ( "retries 5xx from JSON-encoded Error message with status property" , ( ) => {
1047+ const err = new Error ( JSON . stringify ( { status : 500 , message : "server error" } ) )
1048+
1049+ const result = MessageV2 . fromError ( err , { providerID } )
1050+
1051+ expect ( MessageV2 . APIError . isInstance ( result ) ) . toBe ( true )
1052+ expect ( ( result as MessageV2 . APIError ) . data . isRetryable ) . toBe ( true )
1053+ expect ( ( result as MessageV2 . APIError ) . data . statusCode ) . toBe ( 500 )
1054+ } )
1055+
1056+ test ( "retries 5xx from JSON-encoded Error message and extracts inner message" , ( ) => {
1057+ const err = new Error ( JSON . stringify ( { statusCode : 500 , message : "server error" } ) )
1058+
1059+ const result = MessageV2 . fromError ( err , { providerID } )
1060+
1061+ expect ( MessageV2 . APIError . isInstance ( result ) ) . toBe ( true )
1062+ expect ( ( result as MessageV2 . APIError ) . data . isRetryable ) . toBe ( true )
1063+ expect ( ( result as MessageV2 . APIError ) . data . statusCode ) . toBe ( 500 )
1064+ expect ( ( result as MessageV2 . APIError ) . data . message ) . toBe ( "server error" )
1065+ } )
1066+
1067+ test ( "retries 5xx from JSON-encoded Error message with nested response.statusCode" , ( ) => {
1068+ const err = new Error ( JSON . stringify ( { response : { statusCode : 503 } , message : "upstream" } ) )
1069+
1070+ const result = MessageV2 . fromError ( err , { providerID } )
1071+
1072+ expect ( MessageV2 . APIError . isInstance ( result ) ) . toBe ( true )
1073+ expect ( ( result as MessageV2 . APIError ) . data . isRetryable ) . toBe ( true )
1074+ expect ( ( result as MessageV2 . APIError ) . data . statusCode ) . toBe ( 503 )
1075+ expect ( ( result as MessageV2 . APIError ) . data . message ) . toBe ( "upstream" )
1076+ } )
1077+
1078+ test ( "retries 5xx from JSON-encoded Error message with nested response.status" , ( ) => {
1079+ const err = new Error ( JSON . stringify ( { response : { status : 502 } , message : "bad gw" } ) )
1080+
1081+ const result = MessageV2 . fromError ( err , { providerID } )
1082+
1083+ expect ( MessageV2 . APIError . isInstance ( result ) ) . toBe ( true )
1084+ expect ( ( result as MessageV2 . APIError ) . data . isRetryable ) . toBe ( true )
1085+ expect ( ( result as MessageV2 . APIError ) . data . statusCode ) . toBe ( 502 )
1086+ } )
1087+
1088+ test ( "does not retry 4xx from JSON-encoded Error message" , ( ) => {
1089+ const err = new Error ( JSON . stringify ( { statusCode : 422 , message : "invalid request" } ) )
1090+
1091+ const result = MessageV2 . fromError ( err , { providerID } )
1092+
1093+ expect ( result . name ) . toBe ( "UnknownError" )
1094+ } )
1095+
1096+ test ( "retries 5xx from plain object with status property" , ( ) => {
1097+ const err = { status : 500 , message : "Internal Server Error" }
1098+
1099+ const result = MessageV2 . fromError ( err , { providerID } )
1100+
1101+ expect ( MessageV2 . APIError . isInstance ( result ) ) . toBe ( true )
1102+ expect ( ( result as MessageV2 . APIError ) . data . isRetryable ) . toBe ( true )
1103+ expect ( ( result as MessageV2 . APIError ) . data . statusCode ) . toBe ( 500 )
1104+ } )
1105+
1106+ test ( "retries 5xx from plain object with statusCode" , ( ) => {
1107+ const err = { statusCode : 502 , message : "Bad Gateway" }
1108+
1109+ const result = MessageV2 . fromError ( err , { providerID } )
1110+
1111+ expect ( MessageV2 . APIError . isInstance ( result ) ) . toBe ( true )
1112+ expect ( ( result as MessageV2 . APIError ) . data . isRetryable ) . toBe ( true )
1113+ expect ( ( result as MessageV2 . APIError ) . data . statusCode ) . toBe ( 502 )
1114+ } )
1115+
1116+ test ( "retries 5xx from plain object with response.status" , ( ) => {
1117+ const err = { response : { status : 500 } , message : "fail" }
1118+
1119+ const result = MessageV2 . fromError ( err , { providerID } )
1120+
1121+ expect ( MessageV2 . APIError . isInstance ( result ) ) . toBe ( true )
1122+ expect ( ( result as MessageV2 . APIError ) . data . isRetryable ) . toBe ( true )
1123+ } )
1124+
1125+ test ( "retries 5xx from plain object with response.statusCode" , ( ) => {
1126+ const err = { response : { statusCode : 503 } , message : "unavailable" }
1127+
1128+ const result = MessageV2 . fromError ( err , { providerID } )
1129+
1130+ expect ( MessageV2 . APIError . isInstance ( result ) ) . toBe ( true )
1131+ expect ( ( result as MessageV2 . APIError ) . data . isRetryable ) . toBe ( true )
1132+ expect ( ( result as MessageV2 . APIError ) . data . statusCode ) . toBe ( 503 )
1133+ } )
1134+
1135+ test ( "does not retry 4xx from plain object" , ( ) => {
1136+ const err = { statusCode : 404 , message : "Not Found" }
1137+
1138+ const result = MessageV2 . fromError ( err , { providerID } )
1139+
1140+ expect ( result . name ) . toBe ( "UnknownError" )
1141+ } )
1142+
1143+ test ( "does not retry 4xx Error" , ( ) => {
1144+ const err = Object . assign ( new Error ( "Not Found" ) , { statusCode : 404 } )
1145+
1146+ const result = MessageV2 . fromError ( err , { providerID } )
1147+
1148+ expect ( result . name ) . toBe ( "UnknownError" )
1149+ } )
1150+
1151+ test ( "does not retry Error without statusCode" , ( ) => {
1152+ const err = new Error ( "something went wrong" )
1153+
1154+ const result = MessageV2 . fromError ( err , { providerID } )
1155+
1156+ expect ( result . name ) . toBe ( "UnknownError" )
1157+ } )
1158+
10061159 test ( "classifies ZlibError from fetch as retryable APIError" , ( ) => {
10071160 const zlibError = new Error (
10081161 'ZlibError fetching "https://opencode.cloudflare.dev/anthropic/messages". For more information, pass `verbose: true` in the second argument to fetch()' ,
0 commit comments