{"record":{"id":"52d9b8a7f51981f8","repo":"charmbracelet/crush","slug":"could-not-parse-command-w-52d9b8","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/shell.go","lineNumber":273,"sourceCode":"\t}\n}\n\n// execCommon is the shared implementation for executing commands\nfunc (s *Shell) execCommon(ctx context.Context, command string, stdout, stderr io.Writer) (err error) {\n\tvar runner *interp.Runner\n\tdefer func() {\n\t\tif r := recover(); r != nil {\n\t\t\terr = fmt.Errorf(\"command execution panic: %v\", r)\n\t\t}\n\t\tif runner != nil {\n\t\t\ts.updateShellFromRunner(runner)\n\t\t}\n\t\ts.logger.InfoPersist(\"command finished\", \"command\", command, \"err\", err)\n\t}()\n\n\tline, err := syntax.NewParser().Parse(strings.NewReader(command), \"\")\n\tif err != nil {\n\t\treturn fmt.Errorf(\"could not parse command: %w\", err)\n\t}\n\n\trunner, err = s.newInterp(nil, stdout, stderr)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"could not run command: %w\", err)\n\t}\n\n\terr = runner.Run(ctx, line)\n\treturn err\n}\n\n// exec executes commands using a cross-platform shell interpreter.\nfunc (s *Shell) exec(ctx context.Context, command string) (string, string, error) {\n\tvar stdout, stderr bytes.Buffer\n\terr := s.execCommon(ctx, command, &stdout, &stderr)\n\treturn stdout.String(), stderr.String(), err\n}\n","sourceCodeStart":255,"sourceCodeEnd":291,"githubUrl":"https://github.com/charmbracelet/crush/blob/7944b8e52225d8805e31eacbf7ef24856b0dfb7a/internal/shell/shell.go#L255-L291","documentation":"Shell.execCommon (used by both exec and execStream) parses the command string with syntax.NewParser().Parse before interpreting it. A parse failure is wrapped as \"could not parse command: %w\" and the command is never executed. Functionally identical to error 670 but on the persistent Shell type rather than the stateless Run() helper.","triggerScenarios":"Passing shell-invalid source to Shell.exec/execStream: unclosed quotes, unmatched fi/done/esac, invalid parameter expansion like ${var, bad redirection targets, or incomplete command substitution $( without closing.","commonSituations":"Agent/tool-generated commands with broken quoting; template rendering bugs producing half-formed shell; users pasting multi-line snippets into the TUI where a heredoc terminator got lost.","solutions":["Inspect the wrapped mvdan/sh parse error, which includes line/column of the syntax problem.","Fix quoting: wrap variables in double quotes, use single quotes for literal strings.","Validate the command standalone with syntax.NewParser().Parse before handing it to the shell.","Split complex multi-line scripts and execute them stepwise to isolate the broken segment."],"exampleFix":"// before\nsh.Exec(ctx, \"for f in *.go; do echo $f\") // missing done\n// after\nsh.Exec(ctx, \"for f in *.go; do echo \\\"$f\\\"; done\")","handlingStrategy":"validation","validationCode":"func validShell(cmd string) bool {\n\t_, err := syntax.NewParser().Parse(strings.NewReader(cmd), \"\")\n\treturn err == nil\n}","typeGuard":null,"tryCatchPattern":"err := sh.Exec(ctx, cmd)\nif err != nil && strings.HasPrefix(err.Error(), \"could not parse command\") {\n\t// return as invalid-input to user/agent; include wrapped position info\n}","preventionTips":["Quote all variable expansions: \"$var\" not $var.","Run generated commands through a parse check in CI before shipping prompts/tools.","Close all heredocs and control-flow blocks within the same command string.","Reject user-pasted snippets that fail parsing instead of passing them through."],"tags":["shell","parse-error","validation"],"backgroundTag":"shell-parse-error","analyzedSha":"7944b8e52225d8805e31eacbf7ef24856b0dfb7a","analyzedAt":"2026-08-29T12:48:59.079Z","schemaVersion":2},"datasetVersion":"2026-08-29T17:17:51.833Z"}