{"record":{"id":"7568b9007bcbc572","repo":"charmbracelet/crush","slug":"parse-w","errorCode":null,"errorMessage":"parse: %w","messagePattern":"parse: %w","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/shell/expand.go","lineNumber":76,"sourceCode":"//   - Errors wrap the failing inner command's exit code and a bounded\n//     prefix of its stderr. Callers that surface the error to users\n//     should additionally scrub it for the original template text.\nfunc ExpandValue(ctx context.Context, value string, env []string) (string, error) {\n\t// Fast path: a value with no shell metacharacters expands to itself.\n\t// Parsing and running it through the interpreter would produce the\n\t// same string at significant cost. Most config values (literal API\n\t// keys, fixed URLs) hit this path, which matters because ExpandValue\n\t// runs over every provider/MCP/LSP value on each config reload.\n\tif !strings.ContainsAny(value, \"$`\\\\'\\\"\") {\n\t\treturn value, nil\n\t}\n\n\t// Parse the value as a here-doc style word: no word splitting, no\n\t// globbing, but full support for $VAR, ${VAR...}, $(...), and\n\t// quoted/escaped strings.\n\tword, err := syntax.NewParser().Document(strings.NewReader(value))\n\tif err != nil {\n\t\treturn \"\", fmt.Errorf(\"parse: %w\", err)\n\t}\n\n\t// Build a minimal Shell value purely to reuse its handler chain\n\t// (builtins, block funcs, optional Go coreutils) inside $(...).\n\t// We deliberately skip NewShell so the passed-in env is used\n\t// verbatim, with no CRUSH/AGENT/AI_AGENT injection: callers of\n\t// ExpandValue control the env, and nounset must treat any name\n\t// not in env as unset.\n\t//\n\t// The working directory is only needed for $(...) command\n\t// substitution, so we resolve it lazily on first use. Values that\n\t// only reference variables or use quoting (the common case) avoid\n\t// the os.Getwd() syscall entirely.\n\ts := &Shell{\n\t\tenv:    env,\n\t\tlogger: noopLogger{},\n\t}\n\tcwdResolved := false","sourceCodeStart":58,"sourceCodeEnd":94,"githubUrl":"https://github.com/charmbracelet/crush/blob/7944b8e52225d8805e31eacbf7ef24856b0dfb7a/internal/shell/expand.go#L58-L94","documentation":"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.","triggerScenarios":"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.","commonSituations":"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).","solutions":["Fix the shell syntax in the value: balance quotes and parentheses, close ${...} expansions.","Escape literal metacharacters: prefix $ ` \" ' \\ with a backslash so they expand to themselves.","If the value must be taken literally, remove the special characters from the config entirely.","Test the value with `echo '<value>'` in a real shell to confirm it parses."],"exampleFix":"// before\nExpandValue(ctx, \"pass\\word$\", env)\n// after\nExpandValue(ctx, \"pass\\\\word$\", env)","handlingStrategy":"validation","validationCode":"// cheap pre-check mirroring the parser's needs\nif strings.Count(v, \"$\")%2 != 0 || strings.Count(v, \"(\") != strings.Count(v, \")\") {\n    return fmt.Errorf(\"unbalanced expansion syntax in %q\", v)\n}","typeGuard":null,"tryCatchPattern":"expanded, err := shell.ExpandValue(ctx, value, env)\nif err != nil && strings.HasPrefix(err.Error(), \"parse: \") {\n    // invalid shell syntax in config value\n}","preventionTips":["Escape literal $ ` \" ' \\ characters in config values.","Test expansion-heavy values in a real shell first.","Prefer plain values without metacharacters when expansion is not needed.","Validate config with a shell-syntax linter at load time."],"tags":["shell","expansion","parsing","configuration"],"backgroundTag":"shell-expansion-parse-error","analyzedSha":"7944b8e52225d8805e31eacbf7ef24856b0dfb7a","analyzedAt":"2026-08-29T12:48:59.079Z","schemaVersion":2},"datasetVersion":"2026-08-29T17:17:51.833Z"}