Skip to content

Commit 806de38

Browse files
fix: don't retry permanent errors in RunWithRetries (#164)
## Summary `RunWithRetries` in `internal/system/helpers.go` previously wrapped **all** errors with `retry.RetryableError`, meaning permanent failures such as `ErrNotInstalled` (binary not found) were retried with exponential backoff for the full `maxDuration` — typically 5 minutes — before finally being reported. This change: - **Treats `ErrNotInstalled` as a permanent error**, returning it immediately without retrying. This sentinel error is returned by `DryRunWorker` when a read-only command's binary is not on the system, and retrying it cannot succeed. ## Motivation Several call sites pass a 5-minute `maxDuration`. When a command fails with a permanent error (e.g. a snap or binary that simply does not exist), the user experiences an unexplained 5-minute hang before the error is finally surfaced. This is particularly problematic in dry-run mode where `ErrNotInstalled` is the expected signal that a binary is absent. 🤖 Generated with [Claude Code](https://claude.com/claude-code) but owned by me. Co-authored-by: Claude Code <noreply@anthropic.com>
1 parent 6307920 commit 806de38

File tree

1 file changed

+6
-0
lines changed

1 file changed

+6
-0
lines changed

internal/system/helpers.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package system
22

33
import (
44
"context"
5+
"errors"
56
"fmt"
67
"os"
78
"path"
@@ -36,6 +37,8 @@ func RunExclusive(w Worker, c *Command) ([]byte, error) {
3637

3738
// RunWithRetries retries the command using exponential backoff, starting at
3839
// 1 second. Retries will be attempted up to the specified maximum duration.
40+
// Errors that are known to be permanent (e.g. ErrNotInstalled) are returned
41+
// immediately without retrying.
3942
func RunWithRetries(w Worker, c *Command, maxDuration time.Duration) ([]byte, error) {
4043
backoff := retry.NewExponential(1 * time.Second)
4144
backoff = retry.WithMaxDuration(maxDuration, backoff)
@@ -44,6 +47,9 @@ func RunWithRetries(w Worker, c *Command, maxDuration time.Duration) ([]byte, er
4447
return retry.DoValue(ctx, backoff, func(ctx context.Context) ([]byte, error) {
4548
output, err := w.Run(c)
4649
if err != nil {
50+
if errors.Is(err, ErrNotInstalled) {
51+
return nil, err
52+
}
4753
return nil, retry.RetryableError(err)
4854
}
4955

0 commit comments

Comments
 (0)