@@ -104,8 +104,9 @@ describe("createCorbitsRetryPolicy", () => {
104104 raw : { error : { message : "Too Many Requests" } } ,
105105 } ,
106106 } ) ;
107- // Remapped to retryable -> default backoff, not abort on moderate Retry-After.
108- expect ( decision ) . toEqual ( { kind : "retry" , delayMs : 500 } ) ;
107+ // Remapped to retryable -> paced retry honors the server's Retry-After,
108+ // not abort on moderate Retry-After and not a capped 30s wait.
109+ expect ( decision ) . toEqual ( { kind : "retry" , delayMs : 45_000 } ) ;
109110 } ) ;
110111
111112 test ( "stamped Codex usage-limit 429 retries as retryable, not long-quota abort" , async ( ) => {
@@ -120,7 +121,7 @@ describe("createCorbitsRetryPolicy", () => {
120121 raw : "You have hit your ChatGPT usage limit" ,
121122 } ,
122123 } ) ;
123- expect ( decision ) . toEqual ( { kind : "retry" , delayMs : 500 } ) ;
124+ expect ( decision ) . toEqual ( { kind : "retry" , delayMs : 45_000 } ) ;
124125 } ) ;
125126
126127 test ( "stamped xAI usage/quota body still aborts on long retryAfterMs" , async ( ) => {
@@ -174,7 +175,7 @@ describe("createCorbitsRetryPolicy", () => {
174175 } ;
175176 expect ( await decide ( bare429 ) ) . toEqual ( { kind : "abort" } ) ;
176177 current = "xai/thegreataxios" ;
177- expect ( await decide ( bare429 ) ) . toEqual ( { kind : "retry" , delayMs : 500 } ) ;
178+ expect ( await decide ( bare429 ) ) . toEqual ( { kind : "retry" , delayMs : 45_000 } ) ;
178179 } ) ;
179180
180181 // CL-6910: the harness only surfaces `inference.error` to the director
@@ -242,7 +243,7 @@ describe("createCorbitsRetryPolicy", () => {
242243 raw : { error : { message : "Too Many Requests" } } ,
243244 } ,
244245 } ;
245- expect ( await decide ( bare429 ) ) . toEqual ( { kind : "retry" , delayMs : 500 } ) ;
246+ expect ( await decide ( bare429 ) ) . toEqual ( { kind : "retry" , delayMs : 45_000 } ) ;
246247 current = "openai" ;
247248 expect ( await decide ( bare429 ) ) . toEqual ( { kind : "abort" } ) ;
248249 } ) ;
@@ -298,4 +299,63 @@ describe("createCorbitsRetryPolicy", () => {
298299 } ) ;
299300 expect ( notes ) . toHaveLength ( 0 ) ;
300301 } ) ;
302+
303+ test ( "retryable 429 honors Retry-After instead of the fixed 500/1000ms backoff" , async ( ) => {
304+ const decide = policy ( { providerId : "codex/abk-labs" } ) ;
305+ const situation = ( attempt : number ) => ( {
306+ attempt,
307+ elapsedMs : 0 ,
308+ error : {
309+ category : "retryable" as const ,
310+ message : "Too Many Requests" ,
311+ statusCode : 429 ,
312+ retryAfterMs : 5_000 ,
313+ } ,
314+ } ) ;
315+ expect ( await decide ( situation ( 1 ) ) ) . toEqual ( { kind : "retry" , delayMs : 5_000 } ) ;
316+ expect ( await decide ( situation ( 2 ) ) ) . toEqual ( { kind : "retry" , delayMs : 5_000 } ) ;
317+ expect ( await decide ( situation ( 3 ) ) ) . toEqual ( { kind : "abort" } ) ;
318+ } ) ;
319+
320+ test ( "retryable 429 honors a Retry-After above the blind-wait ceiling" , async ( ) => {
321+ const decide = policy ( { providerId : "codex/abk-labs" } ) ;
322+ const decision = await decide ( {
323+ attempt : 1 ,
324+ elapsedMs : 0 ,
325+ error : {
326+ category : "retryable" as const ,
327+ message : "Too Many Requests" ,
328+ statusCode : 429 ,
329+ retryAfterMs : 120_000 ,
330+ } ,
331+ } ) ;
332+ expect ( decision ) . toEqual ( { kind : "retry" , delayMs : 120_000 } ) ;
333+ } ) ;
334+
335+ test ( "retryable 429 with a day-long Retry-After aborts instead of hanging" , async ( ) => {
336+ const decide = policy ( { providerId : "codex/abk-labs" } ) ;
337+ const decision = await decide ( {
338+ attempt : 1 ,
339+ elapsedMs : 0 ,
340+ error : {
341+ category : "retryable" as const ,
342+ message : "Too Many Requests" ,
343+ statusCode : 429 ,
344+ retryAfterMs : 86_400_000 ,
345+ } ,
346+ } ) ;
347+ expect ( decision ) . toEqual ( { kind : "abort" } ) ;
348+ } ) ;
349+
350+ test ( "retryable 429 without Retry-After keeps the fixed backoff" , async ( ) => {
351+ const decide = policy ( ) ;
352+ const situation = ( attempt : number ) => ( {
353+ attempt,
354+ elapsedMs : 0 ,
355+ error : { category : "retryable" as const , message : "boom" , statusCode : 429 } ,
356+ } ) ;
357+ expect ( await decide ( situation ( 1 ) ) ) . toEqual ( { kind : "retry" , delayMs : 500 } ) ;
358+ expect ( await decide ( situation ( 2 ) ) ) . toEqual ( { kind : "retry" , delayMs : 1000 } ) ;
359+ expect ( await decide ( situation ( 3 ) ) ) . toEqual ( { kind : "abort" } ) ;
360+ } ) ;
301361} ) ;
0 commit comments