{"record":{"id":"d392a47aa77f644f","repo":"charmbracelet/crush","slug":"invalid-value-format-s","errorCode":null,"errorMessage":"invalid value format: %s","messagePattern":"invalid value format: (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/config/resolve.go","lineNumber":94,"sourceCode":"// ResolveValue resolves shell-style substitution anywhere in the string:\n//\n//   - $(command) for command substitution, with full quoting and nesting.\n//   - $VAR and ${VAR} for environment variables.\n//   - ${VAR:-default} / ${VAR:+alt} / ${VAR:?msg} for defaulting.\n//\n// Unset variables expand to the empty string by default, matching bash.\n// Command-substitution failures are always a hard error. Required\n// credentials should use ${VAR:?message} so a missing variable fails\n// loudly at load time instead of quietly resolving to empty. Global\n// strict mode is available via shell.NoUnset for callers that want the\n// old nounset-on behaviour back.\nfunc (r *shellVariableResolver) ResolveValue(value string) (string, error) {\n\t// Preserve the historical backward-compat contract: a lone \"$\" is a\n\t// malformed config value, not a legal literal. The underlying shell\n\t// parser would accept it as a literal; we reject it here so existing\n\t// configs that relied on this validation still fail early.\n\tif value == \"$\" {\n\t\treturn \"\", fmt.Errorf(\"invalid value format: %s\", value)\n\t}\n\n\tctx, cancel := context.WithTimeout(context.Background(), resolveTimeout)\n\tdefer cancel()\n\n\tout, err := r.expand(ctx, value, r.env.Env())\n\tif err != nil {\n\t\treturn \"\", sanitizeResolveError(value, err)\n\t}\n\treturn out, nil\n}\n\n// maxResolveErrBytes bounds the size of the inner error message surfaced\n// from a resolution failure. Defense-in-depth on top of shell.ExpandValue's\n// own stderr budget: a custom Expander injected via WithExpander, or any\n// future non-shell error path, must still produce a user-safe message.\nconst maxResolveErrBytes = 512\n","sourceCodeStart":76,"sourceCodeEnd":112,"githubUrl":"https://github.com/charmbracelet/crush/blob/7944b8e52225d8805e31eacbf7ef24856b0dfb7a/internal/config/resolve.go#L76-L112","documentation":"Thrown by shellVariableResolver.ResolveValue when the config value is exactly \"$\". This preserves a backward-compat contract: a lone \"$\" is treated as a malformed config value rather than a literal, even though the underlying shell parser would accept it, so configs relying on validation fail early.","triggerScenarios":"Calling ResolveValue(\"$\") — typically via ConfigStore.Resolve or during config value expansion — with a variable placeholder in crushrc/crush.json that is empty, e.g. `\"${\"`, `\"$\"`, or a truncated env var reference.","commonSituations":"Typo in a config variable reference (\"$\" with no name); a shell script generating the config produced an empty variable; copy-paste truncation of \"${API_KEY}\".","solutions":["Fix the config value to be a full variable reference, e.g. \"${MY_API_KEY}\" or \"$MY_API_KEY\" instead of \"$\".","Remove the malformed value if it was unintentional.","Set the intended environment variable so expansion succeeds after fixing the reference.","If you truly need a literal dollar sign, escape/quote it per the shell config rules (e.g. \"$$\" or '\\$' as supported)."],"exampleFix":"// before (crushrc)\napi_key \"$\"\n// after\napi_key \"${OPENAI_API_KEY}\"","handlingStrategy":"validation","validationCode":"func validValue(v string) error {\n    if v == \"$\" { return errors.New(\"invalid value format: $\") }\n    return nil\n}\n// validate config values before writing them to crushrc","typeGuard":null,"tryCatchPattern":"out, err := resolver.ResolveValue(val)\nif err != nil && strings.HasPrefix(err.Error(), \"invalid value format\") {\n    return fmt.Errorf(\"config value %q is malformed; use a full ${VAR} reference\", val)\n}","preventionTips":["Never write a bare \"$\" as a config value","Validate variable references match ${NAME} or $NAME before saving config","Escape literal dollar signs per shell config rules","Lint generated config files for truncated variables"],"tags":["config","validation","variables"],"backgroundTag":"invalid-config-value","analyzedSha":"7944b8e52225d8805e31eacbf7ef24856b0dfb7a","analyzedAt":"2026-08-29T12:48:59.079Z","schemaVersion":2},"datasetVersion":"2026-08-29T17:17:51.833Z"}