{"record":{"id":"e5f66f4b95d5230a","repo":"charmbracelet/crush","slug":"could-not-parse-command-w","errorCode":null,"errorMessage":"could not parse command: %w","messagePattern":"could not parse command: %w","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/shell/run.go","lineNumber":83,"sourceCode":"\t\t}\n\t}()\n\n\tif opts.Cwd == \"\" {\n\t\treturn fmt.Errorf(\"shell.Run: Cwd is required\")\n\t}\n\n\tstdout := opts.Stdout\n\tif stdout == nil {\n\t\tstdout = io.Discard\n\t}\n\tstderr := opts.Stderr\n\tif stderr == nil {\n\t\tstderr = io.Discard\n\t}\n\n\tline, err := syntax.NewParser().Parse(strings.NewReader(opts.Command), \"\")\n\tif err != nil {\n\t\treturn fmt.Errorf(\"could not parse command: %w\", err)\n\t}\n\n\trunner, err := newRunner(opts.Cwd, opts.Env, opts.Stdin, stdout, stderr, opts.BlockFuncs)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"could not run command: %w\", err)\n\t}\n\n\treturn runner.Run(ctx, line)\n}\n\n// CaptureResult holds the combined output and exit code from a\n// captured shell execution.\ntype CaptureResult struct {\n\tOutput   string\n\tExitCode int\n}\n\n// PersistFunc is a callback that persists a shell command result.","sourceCodeStart":65,"sourceCodeEnd":101,"githubUrl":"https://github.com/charmbracelet/crush/blob/7944b8e52225d8805e31eacbf7ef24856b0dfb7a/internal/shell/run.go#L65-L101","documentation":"Run() in internal/shell/run.go parses the supplied command string with mvdan/sh's syntax.NewParser().Parse before execution. If the shell source cannot be tokenized/parsed (unbalanced quotes, unclosed constructs, invalid syntax), the parse error is wrapped with \"could not parse command: %w\" and returned without running anything. This is a pre-execution validation failure, so no side effects occur.","triggerScenarios":"Calling shell.Run (or opts.Command supplied to it) with a string that is not valid POSIX shell: unterminated single/double quotes, unclosed if/for/heredoc, stray 'fi'/'done', invalid redirection syntax, or a trailing backslash with nothing following.","commonSituations":"Developers interpolating user input or template variables into command strings and accidentally breaking quoting; multi-line commands assembled programmatically where a line continuation or heredoc terminator is missing; commands copy-pasted with smart quotes.","solutions":["Print the command string with %q and check for unbalanced quotes, parentheses, or backslashes before passing it to Run.","Validate the command with syntax.NewParser().Parse(strings.NewReader(cmd), \"\") yourself first and surface a precise parse-error position.","If the command needs multiline constructs (if/for/heredocs), ensure all open blocks are closed in the same string passed to Run.","If the command was intended to run a binary directly, pass argument arrays instead of building a shell string."],"exampleFix":"// before\nshell.Run(ctx, shell.RunOpts{Command: \"echo \\\"hello\"}) // unterminated quote\n// after\nshell.Run(ctx, shell.RunOpts{Command: \"echo \\\"hello\\\"\"})","handlingStrategy":"validation","validationCode":"func validShell(cmd string) bool {\n\t_, err := syntax.NewParser().Parse(strings.NewReader(cmd), \"\")\n\treturn err == nil\n}\nif !validShell(opts.Command) { /* fix quoting before Run */ }","typeGuard":null,"tryCatchPattern":"if err := shell.Run(ctx, opts); err != nil {\n\tvar pe *parseErr // inspect wrapped error via errors.Unwrap / %v\n\tif strings.HasPrefix(err.Error(), \"could not parse command\") {\n\t\t// surface err to the user as invalid input, not a runtime failure\n\t}\n}","preventionTips":["Always single/double-quote interpolated variables in command strings.","Validate commands with syntax.NewParser().Parse in unit tests for every template-generated command.","Prefer passing argument arrays over concatenating shell strings.","Lint generated multi-line scripts for balanced if/fi, for/done pairs."],"tags":["shell","parse-error","input-validation"],"backgroundTag":"shell-parse-error","analyzedSha":"7944b8e52225d8805e31eacbf7ef24856b0dfb7a","analyzedAt":"2026-08-29T12:48:59.079Z","schemaVersion":2},"datasetVersion":"2026-08-29T17:17:51.833Z"}