@@ -26,17 +26,38 @@ func (c *StubHTTPClient) Do(req *http.Request) (*http.Response, error) {
2626
2727func WaitForTask (ctx context.Context , client * Stream , taskID string ) (* StreamResponse [GetTaskResponse ], error ) {
2828 // Poll with progressive intervals: start at 1s, increase by 1s each
29- // attempt up to 5s, for a total ceiling of ~120s . This handles slow
29+ // attempt up to 5s, for a total ceiling of ~190s . This handles slow
3030 // task completion under heavy parallel load while still finishing
3131 // quickly when the server is responsive.
32+ //
33+ // "failed" is treated as non-terminal until it persists for at least
34+ // failedConfirmDuration. The chat backend writes Status="failed" before
35+ // asynq retries rate-limited or transient-internal failures; retries are
36+ // scheduled 10-15s out (rateLimitAwareRetryDelay), and the next attempt
37+ // overwrites the result with "running" then "completed". Any non-failed
38+ // observation resets the deadline so chains of retries don't time out.
3239 const maxAttempts = 40
40+ const failedConfirmDuration = 30 * time .Second
41+ var lastResult * StreamResponse [GetTaskResponse ]
42+ var firstFailedAt time.Time
3343 for i := 0 ; i < maxAttempts ; i ++ {
3444 taskResult , err := client .GetTask (context .Background (), taskID , & GetTaskRequest {})
3545 if err != nil {
3646 return nil , fmt .Errorf ("failed to get task result: %w" , err )
3747 }
38- if taskResult .Data .Status == "completed" || taskResult .Data .Status == "failed" {
48+ lastResult = taskResult
49+ switch taskResult .Data .Status {
50+ case "completed" :
3951 return taskResult , nil
52+ case "failed" :
53+ if firstFailedAt .IsZero () {
54+ firstFailedAt = time .Now ()
55+ }
56+ if time .Since (firstFailedAt ) >= failedConfirmDuration {
57+ return taskResult , nil
58+ }
59+ default :
60+ firstFailedAt = time.Time {}
4061 }
4162
4263 interval := time .Duration (i + 1 ) * time .Second
@@ -50,6 +71,9 @@ func WaitForTask(ctx context.Context, client *Stream, taskID string) (*StreamRes
5071 case <- time .After (interval ):
5172 }
5273 }
74+ if lastResult != nil {
75+ return lastResult , fmt .Errorf ("task %s did not complete after %d attempts (last status %q)" , taskID , maxAttempts , lastResult .Data .Status )
76+ }
5377 return nil , fmt .Errorf ("task %s did not complete after %d attempts" , taskID , maxAttempts )
5478}
5579
0 commit comments