charmbracelet/crush · error

could not parse command: %w

Error message

could not parse command: %w

What it means

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.

Source

Thrown at internal/shell/shell.go:273

	}
}

// execCommon is the shared implementation for executing commands
func (s *Shell) execCommon(ctx context.Context, command string, stdout, stderr io.Writer) (err error) {
	var runner *interp.Runner
	defer func() {
		if r := recover(); r != nil {
			err = fmt.Errorf("command execution panic: %v", r)
		}
		if runner != nil {
			s.updateShellFromRunner(runner)
		}
		s.logger.InfoPersist("command finished", "command", command, "err", err)
	}()

	line, err := syntax.NewParser().Parse(strings.NewReader(command), "")
	if err != nil {
		return fmt.Errorf("could not parse command: %w", err)
	}

	runner, err = s.newInterp(nil, stdout, stderr)
	if err != nil {
		return fmt.Errorf("could not run command: %w", err)
	}

	err = runner.Run(ctx, line)
	return err
}

// exec executes commands using a cross-platform shell interpreter.
func (s *Shell) exec(ctx context.Context, command string) (string, string, error) {
	var stdout, stderr bytes.Buffer
	err := s.execCommon(ctx, command, &stdout, &stderr)
	return stdout.String(), stderr.String(), err
}

View on GitHub (pinned to 7944b8e522)

Solutions

  1. Inspect the wrapped mvdan/sh parse error, which includes line/column of the syntax problem.
  2. Fix quoting: wrap variables in double quotes, use single quotes for literal strings.
  3. Validate the command standalone with syntax.NewParser().Parse before handing it to the shell.
  4. Split complex multi-line scripts and execute them stepwise to isolate the broken segment.

Example fix

// before
sh.Exec(ctx, "for f in *.go; do echo $f") // missing done
// after
sh.Exec(ctx, "for f in *.go; do echo \"$f\"; done")
Defensive patterns

Strategy: validation

Validate before calling

func validShell(cmd string) bool {
	_, err := syntax.NewParser().Parse(strings.NewReader(cmd), "")
	return err == nil
}

Try / catch

err := sh.Exec(ctx, cmd)
if err != nil && strings.HasPrefix(err.Error(), "could not parse command") {
	// return as invalid-input to user/agent; include wrapped position info
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of charmbracelet/crush@7944b8e522 (2026-08-29). Data as JSON: /api/errors/52d9b8a7f51981f8. Report an issue: GitHub.