charmbracelet/crush · error
could not parse %s: %w
Error message
could not parse %s: %w
What it means
runShellSource wraps a syntax parse failure when executing a file's contents as POSIX shell in-process via mvdan.cc/sh. The file could not be parsed by syntax.NewParser().Parse, so the wrapped interpreter never runs it. The underlying mvdan.cc/sh parse error is included via %w.
Source
Thrown at internal/shell/dispatch.go:386
// runShellSource parses path's contents as POSIX shell and runs it
// in-process via a nested interp.Runner. It reuses the parent runner's cwd,
// env, and stdio, and rebuilds the Crush handler stack so builtins and the
// dispatch handler itself remain available to anything the script invokes.
// Positional parameters ($1, $2, …) come from args[1:].
//
// This is the only branch that reads the full file; probeFile keeps its
// read to probeWindow bytes so the binary/shebang paths never touch more
// than 128 bytes of I/O.
func runShellSource(ctx context.Context, path string, args []string, blockFuncs []BlockFunc) error {
data, err := os.ReadFile(path)
if err != nil {
return err
}
file, err := syntax.NewParser().Parse(bytes.NewReader(data), path)
if err != nil {
return fmt.Errorf("could not parse %s: %w", path, err)
}
hc := interp.HandlerCtx(ctx)
opts := []interp.RunnerOption{
interp.StdIO(hc.Stdin, hc.Stdout, hc.Stderr),
interp.Interactive(false),
interp.Env(hc.Env),
interp.Dir(hc.Dir),
execHandlerOption(blockFuncs),
}
if len(args) > 1 {
// Params with a leading "--" avoids any of args[1:] being
// misinterpreted as set-options (e.g. a user passing "-e" as
// a positional arg to their script).
params := append([]string{"--"}, args[1:]...)
opts = append(opts, interp.Params(params...))
}View on GitHub (pinned to 7944b8e522)
Solutions
- Read the wrapped mvdan.cc/sh error (includes line/column) and fix the syntax at that location.
- Validate the script locally with `sh -n script.sh` (or bash -n if bashisms are intended) before shipping.
- If the script requires bash-only syntax, ensure the shebang targets bash and the dispatcher executes it as such.
- Regenerate or re-download the file if it appears truncated or templated incorrectly.
Example fix
// before (invalid: unmatched quote) echo "hello world // after echo "hello world"
Defensive patterns
Strategy: try-catch
Validate before calling
if err := exec.Command("sh", "-n", scriptPath).Run(); err != nil {
// script has syntax problems; fix before executing via dispatcher
} Try / catch
if err != nil {
if strings.HasPrefix(err.Error(), "could not parse ") {
// parse failure: log path + wrapped mvdan.cc/sh position info
}
} Prevention
- Run `sh -n` (or bash -n) on scripts in CI.
- Keep scripts POSIX-compatible or target bash explicitly.
- Avoid generating scripts via string templating without validation.
- Watch for CRLF/encoding corruption when scripts cross editors.
When it happens
Trigger: dispatchShebang or the binary/exec handler resolves a script to shell source and runShellSource reads it; the file contains syntax invalid for mvdan.cc/sh (POSIX-ish shell), such as unbalanced quotes, unsupported bashisms, or heredoc mistakes.
Common situations: Scripts using bash-4+ features the parser rejects; corrupted scripts from interrupted downloads; CRLF or encoding issues producing stray characters; scripts generated with template placeholders left unexpanded.
Related errors
AI-assisted analysis of charmbracelet/crush@7944b8e522 (2026-08-29).
Data as JSON: /api/errors/3cd3700478bd4f26.
Report an issue: GitHub.