alibaba/open-code-review · error
%s timed out after %s: %w
Error message
%s timed out after %s: %w
What it means
resolveKeyCmd runs a user-configured shell command to fetch a credential under a fixed timeout. When the command's context expires with context.DeadlineExceeded, the error reports the timeout explicitly (wrapping ctx.Err() so errors.Is works) instead of surfacing the SIGKILL exit status the kill produces.
Source
Thrown at internal/llm/keycmd.go:89
c.Stdin = os.Stdin
// Buffer stdout through cappedBuffer rather than an *os.File so os/exec does
// the copying in its own goroutine: that is what lets WaitDelay force the
// pipe closed. exec.CommandContext SIGKILLs only the shell, so a grandchild
// (gpg-agent, pinentry, `op`) that inherited the stdout pipe keeps it open
// and Wait blocks on the read long past the timeout -- reproducible with
// api_key_cmd = "sleep 200 & printf tok". WaitDelay makes Wait give up
// shortly after the context dies.
out := &cappedBuffer{max: keyCmdMaxOutput}
c.Stdout = out
c.WaitDelay = keyCmdWaitDelay
err := c.Run()
// Checked first so a timeout reports as such instead of as the SIGKILL exit
// status it produces. (Run has already joined every stdout copier, so the
// buffer below is safe to read on all paths.)
if ctx.Err() == context.DeadlineExceeded {
// Wrap ctx.Err() so callers can errors.Is(err, context.DeadlineExceeded).
return "", fmt.Errorf("%s timed out after %s: %w", label, keyCmdTimeout, ctx.Err())
}
if out.overflow {
return "", fmt.Errorf("%s produced more than 64KiB of output", label)
}
// ErrWaitDelay only means an orphaned grandchild still holds the pipe; the
// command itself exited fine and its output is already buffered, so use it
// rather than surfacing an exec-internal error.
if err != nil && !errors.Is(err, exec.ErrWaitDelay) {
// Covers non-zero exit and command-not-found (the shell exits non-zero
// and prints its not-found message on the child's stderr). ExitError.Stderr
// stays nil because we assigned c.Stderr, so no output can leak here.
return "", fmt.Errorf("%s failed: %w", label, err)
}
// Trim a trailing line break; multi-line output past that is ambiguous and refused.
// ContainsAny (not Contains "\n") so a lone interior CR is caught too: TrimRight
// leaves it, TrimSpace below only strips the edges, and a CR inside a credential
// makes net/http reject the Authorization header with an opaque error.View on GitHub (pinned to 5cf97d0d15)
Solutions
- Pre-authenticate the command (refresh AWS SSO/session, unlock the agent) so it returns quickly
- Run the command manually and time it; fix whatever hangs (prompt, network, wrong host)
- Check whether the command waits on stdin and remove that requirement
- If the command is legitimately slow, switch to a faster credential path — the loader timeout is fixed
Example fix
// before (hangs waiting for passphrase) keyCmd: "pass show api-key" // after (non-interactive source) keyCmd: "cat ~/.secrets/api-key"
Defensive patterns
Strategy: try-catch
Validate before calling
// pre-test helper latency and non-interactivity
if err := exec.Command("sh", "-c", "timeout 5 <your-key-cmd>").Run(); err != nil {
// hangs or too slow; fix before relying on it
} Type guard
func isTimeout(err error) bool { return errors.Is(err, context.DeadlineExceeded) } Try / catch
key, err := resolveKeyCmd(ctx, cfg)
if errors.Is(err, context.DeadlineExceeded) {
return fmt.Errorf("credential helper hangs or is too slow: %w", err)
} Prevention
- Ensure the helper never prompts interactively (no TTY in automation)
- Pre-authenticate upstream sessions (AWS SSO, gpg-agent) before running
- Test the command under `timeout` to catch hangs early
When it happens
Trigger: The key-retrieval command exceeds keyCmdTimeout — e.g. an aws/codeartifact CLI, a secrets-manager CLI, or an ssh-locked helper that hangs waiting for input or a slow network call.
Common situations: Expired AWS session making a CLI hang or retry slowly; a credential helper prompting interactively (passphrase) with no TTY; DNS/network stalls; wrong remote host in the command.
Understand the failure class
- Timeouts: ETIMEDOUT, deadlines, and hung requests — what actually expires when a request times out.
Related errors
- %s produced more than 64KiB of output
- %s failed: %w
- %s produced multi-line output; expected a single credential
- %s produced a control byte 0x%02X at offset %d; a credential
- %s produced empty output
AI-assisted analysis of alibaba/open-code-review@5cf97d0d15 (2026-09-02).
Data as JSON: /api/errors/c4193e766515e2f6.
Report an issue: GitHub.