diff --git a/internal/github/client.go b/internal/github/client.go index 5d4dede..e25fd29 100644 --- a/internal/github/client.go +++ b/internal/github/client.go @@ -301,12 +301,16 @@ func isRetryableError(err error) bool { "timeout", "temporary failure", "server error", + "stream error", + "CANCEL", + "EOF", + "broken pipe", "502", "503", "504", } for _, msg := range retryableMessages { - if strings.Contains(strings.ToLower(errStr), msg) { + if strings.Contains(strings.ToLower(errStr), strings.ToLower(msg)) { return true } } diff --git a/internal/github/graphql.go b/internal/github/graphql.go index 5f6a3cd..d8b8a05 100644 --- a/internal/github/graphql.go +++ b/internal/github/graphql.go @@ -5,6 +5,7 @@ import ( "fmt" "io" "os" + "strings" "time" "github.com/charmbracelet/bubbles/progress" @@ -133,8 +134,28 @@ func fetchGQLPaginated[Q any, T any, R any]( } for { - if err := client.Query(ctx, config.Query, variables); err != nil { - return nil, fmt.Errorf("graphql query failed: %w", err) + // Retry logic for transient errors + var queryErr error + for retries := 0; retries < 3; retries++ { + queryErr = client.Query(ctx, config.Query, variables) + if queryErr == nil { + break + } + // Check if error is retryable + if !isGQLRetryableError(queryErr) { + break + } + // Wait before retry with exponential backoff + backoff := time.Duration(1<