Skip to content

fix/fallback#520

Merged
SuperCoolPencil merged 10 commits into
mainfrom
fix/fallback
Jun 29, 2026
Merged

fix/fallback#520
SuperCoolPencil merged 10 commits into
mainfrom
fix/fallback

Conversation

@SuperCoolPencil

@SuperCoolPencil SuperCoolPencil commented Jun 26, 2026

Copy link
Copy Markdown
Member
  • fix: broken fallback to single downloader
  • fix: ensure ActiveWorkers is cleared after download completion
  • fix: enhance debug logging for single-threaded downloads
  • fix: add debug logging for Surge version and commit in global state initialization
  • fix: track pending bytes in progressReader for accurate chunk status updates
  • fix: implement context cancellation for single downloader requests
  • '
  • feat: single downloader Retry-After
  • feat: single downloader Retry-After

Greptile Summary

This PR adds rate-limit awareness to the single-threaded downloader: it reads the Retry-After header on HTTP 429/503 responses, waits out the backoff, and surfaces "Rate limited, retrying…" in the TUI instead of silently stalling. A new RateLimited atomic.Bool is threaded from ProgressState through ProgressMsg to DownloadModel.

  • Adds RateLimited atomic.Bool to ProgressState (reset in SessionReset) and propagates it via ProgressMsgDownloadModel to drive a new TUI display branch.
  • Implements Retry-After parsing in SingleDownloader.Download(), toggling the flag before/after the backoff sleep with context-aware cancellation support.
  • Adds the "Rate limited, retrying…" view state in view.go, inserted correctly before the generic paused/zero-speed branch.

Confidence Score: 3/5

Mostly safe, but the RateLimited flag is not cleared when the download is paused or cancelled mid-backoff, leaving the TUI stuck on "Rate limited, retrying…" after the download stops.

The backoff cancel path in downloader.go returns dlCtx.Err() before reaching the RateLimited.Store(false) call. Any pause or abort while the downloader is sleeping will leave the flag set indefinitely, since there are no further progress messages to clear it and SessionReset is only called on a full restart.

internal/engine/single/downloader.go — the rate-limit backoff cancel path needs a defer to clear the RateLimited flag on function exit.

Important Files Changed

Filename Overview
internal/engine/single/downloader.go Adds Retry-After / rate-limit handling with RateLimited state signalling; the flag is correctly set before the backoff but not cleared when the context is cancelled during the wait, leaving the TUI in a stale state.
internal/engine/types/progress.go Adds RateLimited atomic.Bool to ProgressState and resets it in SessionReset(); implementation is clean and consistent with the existing atomic fields pattern.
internal/engine/events/events.go Adds RateLimited bool to ProgressMsg; straightforward struct field addition with no issues.
internal/core/local_service.go Propagates cfg.State.RateLimited.Load() into the outgoing ProgressMsg; correct and minimal change.
internal/tui/model.go Adds rateLimited bool field to DownloadModel; alignment-only formatting change alongside the new field.
internal/tui/process.go Copies msg.RateLimited into d.rateLimited during progress processing; correct single-line change.
internal/tui/view.go Adds "Rate limited, retrying…" display branch; correct placement in the priority chain, though it will display stale text if RateLimited is not cleared on cancellation (see downloader.go finding).
Prompt To Fix All With AI
Fix the following 1 code review issue. Work through them one at a time, proposing concise fixes.

---

### Issue 1 of 1
internal/engine/single/downloader.go:147-155
**`RateLimited` flag not cleared on context cancellation**

When the parent context is cancelled (e.g., user pauses or aborts) while the downloader is waiting out the rate-limit backoff, `dlCtx.Done()` fires and the function returns `dlCtx.Err()` at line 149 — skipping the `RateLimited.Store(false)` call at lines 152-154. The flag remains `true` in the shared `ProgressState`, so the TUI will continue showing "Rate limited, retrying…" even after the download has stopped. Adding `defer d.State.RateLimited.Store(false)` alongside the existing `defer d.State.ActiveWorkers.Store(0)` block (around line 86) would clean this up unconditionally on function exit.

Reviews (1): Last reviewed commit: "Merge branch 'main' into fix/fallback" | Re-trigger Greptile

Greptile also left 1 inline comment on this PR.

@github-actions

Copy link
Copy Markdown

❌ Test Failures on windows-latest

  • github.com/SurgeDM/Surge/cmd: TestRmClean_Offline_Works

Comment on lines 147 to 155
select {
case <-dlCtx.Done():
return dlCtx.Err()
case <-time.After(ra):
}
if d.State != nil {
d.State.RateLimited.Store(false)
}
continue

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 RateLimited flag not cleared on context cancellation

When the parent context is cancelled (e.g., user pauses or aborts) while the downloader is waiting out the rate-limit backoff, dlCtx.Done() fires and the function returns dlCtx.Err() at line 149 — skipping the RateLimited.Store(false) call at lines 152-154. The flag remains true in the shared ProgressState, so the TUI will continue showing "Rate limited, retrying…" even after the download has stopped. Adding defer d.State.RateLimited.Store(false) alongside the existing defer d.State.ActiveWorkers.Store(0) block (around line 86) would clean this up unconditionally on function exit.

Prompt To Fix With AI
This is a comment left during a code review.
Path: internal/engine/single/downloader.go
Line: 147-155

Comment:
**`RateLimited` flag not cleared on context cancellation**

When the parent context is cancelled (e.g., user pauses or aborts) while the downloader is waiting out the rate-limit backoff, `dlCtx.Done()` fires and the function returns `dlCtx.Err()` at line 149 — skipping the `RateLimited.Store(false)` call at lines 152-154. The flag remains `true` in the shared `ProgressState`, so the TUI will continue showing "Rate limited, retrying…" even after the download has stopped. Adding `defer d.State.RateLimited.Store(false)` alongside the existing `defer d.State.ActiveWorkers.Store(0)` block (around line 86) would clean this up unconditionally on function exit.

How can I resolve this? If you propose a fix, please make it concise.

@SuperCoolPencil SuperCoolPencil merged commit ea3b5a4 into main Jun 29, 2026
13 of 14 checks passed
@SuperCoolPencil SuperCoolPencil deleted the fix/fallback branch June 29, 2026 09:43
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant