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

  1. Read the appended stderr text — it is the failing inner command's own diagnostic; fix the underlying command.
  2. Verify any binaries referenced inside $(...) exist in the environment ExpandValue runs in.
  3. Ensure commands in substitutions are safe for a non-interactive environment (no TTY assumptions).
  4. 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

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.