charmbracelet/crush · error
command execution panic: %v
Error message
command execution panic: %v
What it means
shell.Run recovers any panic raised during command execution and converts it into this error instead of crashing the process. The deferred recover in Run captures the panic value r and returns fmt.Errorf("command execution panic: %v", r), so callers see a normal error rather than a program crash.
Source
Thrown at internal/shell/run.go:64
// TermWidth is the terminal width in columns for PTY execution.
// Zero uses a default of 200.
TermWidth int
}
// Run parses and executes a shell command using the same mvdan.cc/sh
// interpreter stack that the stateful [Shell] type uses (builtins,
// optional block list, optional Go coreutils). It is safe to call
// concurrently from multiple goroutines: each call builds its own
// [interp.Runner] and shares no state with other callers or with any
// [Shell] instance.
//
// Errors returned from the command itself (non-zero exit, context
// cancellation, parse failures) follow the same conventions as
// [Shell.Exec]: inspect with [IsInterrupt] and [ExitCode].
func Run(ctx context.Context, opts RunOptions) (err error) {
defer func() {
if r := recover(); r != nil {
err = fmt.Errorf("command execution panic: %v", r)
}
}()
if opts.Cwd == "" {
return fmt.Errorf("shell.Run: Cwd is required")
}
stdout := opts.Stdout
if stdout == nil {
stdout = io.Discard
}
stderr := opts.Stderr
if stderr == nil {
stderr = io.Discard
}
line, err := syntax.NewParser().Parse(strings.NewReader(opts.Command), "")
if err != nil {View on GitHub (pinned to 7944b8e522)
Solutions
- Inspect the panic message and stack (enable verbose logging) to find the panicking component.
- If you supply custom builtins or BlockFuncs, add nil checks and argument validation so they cannot panic.
- Ensure Stdin/Stdout/Stderr writers are safe for concurrent use if Run is called from multiple goroutines.
- Reproduce with a minimal Command and options set, then report upstream if the panic is inside mvdan.cc/sh.
Example fix
// before
Run(ctx, RunOptions{Command: "mybuiltin", Cwd: "/tmp", BlockFuncs: []BlockFunc{nil}})
// after
Run(ctx, RunOptions{Command: "mybuiltin", Cwd: "/tmp", BlockFuncs: []BlockFunc{safeMatcher}}) Defensive patterns
Strategy: try-catch
Try / catch
if err := shell.Run(ctx, opts); err != nil {
if strings.HasPrefix(err.Error(), "command execution panic: ") {
log.Printf("shell.Run panicked: %v", err) // plus capture stack via debug.Stack in wrappers
}
} Prevention
- Wrap custom builtins and BlockFuncs with recover + nil checks.
- Fuzz custom handlers with unexpected arguments.
- Use concurrency-safe writers for Stdout/Stderr.
- Pin and test mvdan.cc/sh versions in CI.
When it happens
Trigger: Any panic inside Run's execution path — the mvdan.cc/sh interpreter, custom builtins, BlockFunc matchers, or writer implementations passed via RunOptions panicking (e.g. nil map write, index out of range) during a registered-builtin or blocked-command run.
Common situations: A custom builtin handler panics on unexpected arguments; a BlockFunc dereferences nil; a bug in a PTY/writer wrapper; concurrent misuse of a non-thread-safe writer passed as Stdout.
Related errors
AI-assisted analysis of charmbracelet/crush@7944b8e522 (2026-08-29).
Data as JSON: /api/errors/b7930b5fd71b1983.
Report an issue: GitHub.