charmbracelet/crush · error
%w: %s
Error message
%w: %s
What it means
wrapCmdSubstErr augments a failed command substitution error with a sanitized, bounded prefix of the inner command's stderr. When a $(...) inside an expanded value fails (non-zero exit or interpreter error), the original error is wrapped via %w and the scrubbed stderr text is appended after a colon.
Source
Thrown at internal/shell/expand.go:142
if rerr := runner.Run(ctx, &syntax.File{Stmts: cs.Stmts}); rerr != nil {
return wrapCmdSubstErr(rerr, stderrBuf.Bytes())
}
return nil
},
// ReadDir / ReadDir2 left nil: globbing is disabled.
}
return expand.Document(cfg, word)
}
// wrapCmdSubstErr attaches a bounded prefix of the inner command's stderr
// to the original error, if any.
func wrapCmdSubstErr(err error, stderrBytes []byte) error {
msg := sanitizeStderr(stderrBytes)
if msg == "" {
return err
}
return fmt.Errorf("%w: %s", err, msg)
}
// sanitizeStderr trims, bounds, and scrubs non-printable bytes from the
// stderr of a failing command so the result is safe to include in an
// error message shown to the user.
func sanitizeStderr(b []byte) string {
b = bytes.TrimRight(b, "\n")
if len(b) > maxInnerStderrBytes {
b = b[:maxInnerStderrBytes]
}
out := make([]byte, len(b))
for i, c := range b {
if c == '\t' || c == '\n' || (c >= 0x20 && c < 0x7f) {
out[i] = c
} else {
out[i] = '?'
}
}View on GitHub (pinned to 7944b8e522)
Solutions
- Read the appended stderr text — it is the failing inner command's own diagnostic; fix the underlying command.
- Verify any binaries referenced inside $(...) exist in the environment ExpandValue runs in.
- Ensure commands in substitutions are safe for a non-interactive environment (no TTY assumptions).
- Match on the wrapped error with errors.Is/As for exit codes (interp.ExitStatus) to distinguish exit failures from other errors.
Defensive patterns
Strategy: try-catch
Validate before calling
// verify inner command works before embedding it in a config value
if err := exec.Command("git", "rev-parse", "--git-dir").Run(); err != nil {
// $(git ...) substitution would fail during ExpandValue
} Try / catch
if err != nil {
var xe interp.ExitStatus
if errors.As(err, &xe) {
// substitution command exited non-zero; appended stderr has details
}
} Prevention
- Test commands embedded in $() inside the target environment.
- Keep stderr in inner commands meaningful; it is appended to the error.
- Avoid substitutions dependent on TTY or interactive state.
- Keep the wrapped error (errors.Is/As) for exit-code inspection.
When it happens
Trigger: ExpandValue evaluates a $(...) command substitution; the inner runner returns an error and its captured stderr is non-empty after sanitizeStderr (trim trailing newlines, bound length, replace non-printables with '?'). Empty stderr leaves the original error untouched.
Common situations: Config values embedding $(git rev-parse ...) or similar in repos where the command fails (not a git repo, missing binary) and prints a diagnostic to stderr; the appended text is the tool's own error message.
Related errors
AI-assisted analysis of charmbracelet/crush@7944b8e522 (2026-08-29).
Data as JSON: /api/errors/9aa510e278e65f46.
Report an issue: GitHub.