charmbracelet/crush · error

parse: %w

Error message

parse: %w

What it means

ExpandValue failed to parse its input as a shell document word. ExpandValue expands a config-style string with full $VAR, ${VAR...}, and $(...) support by parsing it with syntax.NewParser().Document; input containing shell metacharacters ($ ` \ ' ") that is not valid shell word syntax produces this wrapped parse error.

Source

Thrown at internal/shell/expand.go:76

//   - Errors wrap the failing inner command's exit code and a bounded
//     prefix of its stderr. Callers that surface the error to users
//     should additionally scrub it for the original template text.
func ExpandValue(ctx context.Context, value string, env []string) (string, error) {
	// Fast path: a value with no shell metacharacters expands to itself.
	// Parsing and running it through the interpreter would produce the
	// same string at significant cost. Most config values (literal API
	// keys, fixed URLs) hit this path, which matters because ExpandValue
	// runs over every provider/MCP/LSP value on each config reload.
	if !strings.ContainsAny(value, "$`\\'\"") {
		return value, nil
	}

	// Parse the value as a here-doc style word: no word splitting, no
	// globbing, but full support for $VAR, ${VAR...}, $(...), and
	// quoted/escaped strings.
	word, err := syntax.NewParser().Document(strings.NewReader(value))
	if err != nil {
		return "", fmt.Errorf("parse: %w", err)
	}

	// Build a minimal Shell value purely to reuse its handler chain
	// (builtins, block funcs, optional Go coreutils) inside $(...).
	// We deliberately skip NewShell so the passed-in env is used
	// verbatim, with no CRUSH/AGENT/AI_AGENT injection: callers of
	// ExpandValue control the env, and nounset must treat any name
	// not in env as unset.
	//
	// The working directory is only needed for $(...) command
	// substitution, so we resolve it lazily on first use. Values that
	// only reference variables or use quoting (the common case) avoid
	// the os.Getwd() syscall entirely.
	s := &Shell{
		env:    env,
		logger: noopLogger{},
	}
	cwdResolved := false

View on GitHub (pinned to 7944b8e522)

Solutions

  1. Fix the shell syntax in the value: balance quotes and parentheses, close ${...} expansions.
  2. Escape literal metacharacters: prefix $ ` " ' \ with a backslash so they expand to themselves.
  3. If the value must be taken literally, remove the special characters from the config entirely.
  4. Test the value with `echo '<value>'` in a real shell to confirm it parses.

Example fix

// before
ExpandValue(ctx, "pass\word$", env)
// after
ExpandValue(ctx, "pass\\word$", env)
Defensive patterns

Strategy: validation

Validate before calling

// cheap pre-check mirroring the parser's needs
if strings.Count(v, "$")%2 != 0 || strings.Count(v, "(") != strings.Count(v, ")") {
    return fmt.Errorf("unbalanced expansion syntax in %q", v)
}

Try / catch

expanded, err := shell.ExpandValue(ctx, value, env)
if err != nil && strings.HasPrefix(err.Error(), "parse: ") {
    // invalid shell syntax in config value
}

Prevention

When it happens

Trigger: Calling shell.ExpandValue(ctx, value, env) with a value containing metacharacters that don't parse as a shell word — e.g. an unclosed quote, an unterminated $(, or a stray backslash; values without metacharacters take the fast path and never parse.

Common situations: Config values with literal quotes or dollars that were meant verbatim but are invalid shell syntax; API keys or secrets pasted with special characters; YAML/JSON config strings with accidental backslashes; typos like ${VAR (missing brace).

Related errors


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