Skip to content
This repository was archived by the owner on Apr 2, 2026. It is now read-only.

Commit 57756c9

Browse files
greynewellclaude
andcommitted
fix: fail fast on API connection errors instead of retrying for 10 minutes
When the Supermodel API is unreachable, pollJob was retrying connection errors (connection refused, DNS failure, network down) every 10 seconds for the full context duration — up to 10 minutes — before giving up. This blocked the Claude Code Stop hook for the entire outage window. Connection errors are fundamentally different from job-processing delays: - "pending"/"processing" status → API is working, polling makes sense - Connection error → API is unreachable, retrying won't help Change pollJob to return immediately on connection-level errors so the Stop hook can call silentExit() and unblock the session without waiting for the context deadline. 5xx errors, rate limits, and job-in-progress responses continue to be retried as before. Co-Authored-By: Grey Newell <greyshipscode@gmail.com> Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent 7c48d8f commit 57756c9

2 files changed

Lines changed: 12 additions & 15 deletions

File tree

cmd/run.go

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -226,13 +226,10 @@ func runHandler(cmd *cobra.Command, args []string) error {
226226
}(dbPath, proj.Hash, proj.Name, proj.RootDir)
227227
}
228228

229-
// If no cache or forced refresh, fetch from API.
230-
// Use a short timeout so the Stop hook never blocks a Claude Code session
231-
// for more than ~90 seconds during an API outage. Long-running first-time
232-
// fetches for large repos are handled by the background pregen hook.
229+
// If no cache or forced refresh, fetch from API
233230
if graph == nil || forceRefresh {
234231
logFn("[debug] fetching from Supermodel API...")
235-
ctx, cancel := context.WithTimeout(context.Background(), 90*time.Second)
232+
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Minute)
236233
defer cancel()
237234

238235
zipData, skipReport, err := zip.RepoZip(ctx, proj.RootDir)
@@ -472,10 +469,8 @@ func runLocalMode(logFn func(string, ...interface{})) error {
472469
}
473470

474471
// runWithoutCache attempts an API fetch with no cache fallback.
475-
// Uses a short timeout so the Stop hook never blocks a Claude Code session
476-
// for more than ~90 seconds during an API outage.
477472
func runWithoutCache(cfg *config.Config, proj *project.Info, wm *project.WorkingMemory, snap *snapshot.SessionSnapshot, postCompact bool, logFn func(string, ...interface{})) error {
478-
ctx, cancel := context.WithTimeout(context.Background(), 90*time.Second)
473+
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Minute)
479474
defer cancel()
480475

481476
zipData, skipReport, err := zip.RepoZip(ctx, proj.RootDir)

internal/api/client.go

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -371,13 +371,15 @@ func (c *Client) pollJob(
371371

372372
resp, err := c.httpClient.Do(req)
373373
if err != nil {
374-
c.logFn("[warn] poll attempt %d (%s): request error (will retry): %v", attempt+1, endpoint, err)
375-
select {
376-
case <-ctx.Done():
377-
return ctxDeadlineErr(ctx)
378-
case <-c.afterFn(10 * time.Second):
379-
}
380-
continue
374+
// Connection-level errors (DNS failure, connection refused, network
375+
// unreachable) mean the API is down. Retrying won't help and would
376+
// block the caller — typically the Claude Code Stop hook — for the
377+
// full context duration. Return immediately so the hook can exit
378+
// gracefully rather than hanging until the context deadline fires.
379+
// This is distinct from HTTP-level errors (5xx, rate limits) and
380+
// job-processing delays ("pending"/"processing"), which do warrant
381+
// polling retries.
382+
return fmt.Errorf("API unreachable: %w", err)
381383
}
382384
respBody, readErr := io.ReadAll(io.LimitReader(resp.Body, maxResponseSize))
383385
resp.Body.Close()

0 commit comments

Comments
 (0)