charmbracelet/crush · error
%s
Error message
%s
What it means
usage() is the shared helper in internal/shellconfig that prints a usage message to stderr and returns it as an error. Every crushrc builtin handler (applyFlags, handleHook, hookAdd, hookRemove, handleLSP, lspAdd) calls it when arguments are malformed, so this error means one of the shell builtins (provider, model, mcp, lsp, permissions, hook, option) was invoked with the wrong arguments while evaluating a crushrc config file. The message text is the usage string itself, so the error text doubles as documentation.
Source
Thrown at internal/shellconfig/register.go:34
"io"
"github.com/charmbracelet/crush/internal/shell"
)
func init() {
shell.RegisterBuiltin("provider", handleProvider)
shell.RegisterBuiltin("model", handleModel)
shell.RegisterBuiltin("mcp", handleMCP)
shell.RegisterBuiltin("lsp", handleLSP)
shell.RegisterBuiltin("permissions", handlePermissions)
shell.RegisterBuiltin("hook", handleHook)
shell.RegisterBuiltin("option", handleOption)
}
// usage prints a usage message to stderr and returns an error.
func usage(stderr io.Writer, msg string) error {
fmt.Fprintln(stderr, msg)
return fmt.Errorf("%s", msg)
}
// appendArr appends value to the string slice stored at m[key], creating it if
// needed, and returns the result.
func appendArr(m map[string]any, key, value string) []any {
arr, _ := m[key].([]any)
return append(arr, value)
}
View on GitHub (pinned to 7944b8e522)
Solutions
- Read the usage message printed to stderr — it states the exact expected syntax for the builtin that failed
- Open the crushrc file and fix the offending builtin invocation to match the documented syntax
- Check HOOKS.md / internal/shellconfig docs for the current builtin signatures (syntax may have changed between versions)
- Run the config through 'crush' startup again to confirm the error is gone
Example fix
// before (.crushrc) hook add my-hook // after (.crushrc) — supply the required event and command arguments hook add pre_tool_use ./check.sh
Defensive patterns
Strategy: validation
Validate before calling
// Validate a crushrc line before running it
func validArgs(name string, args []string) error {
switch name {
case "hook":
if len(args) < 2 { return errors.New("usage: hook <add|remove> <id ...>") }
case "lsp":
if len(args) < 1 { return errors.New("usage: lsp <add|...> ...") }
case "option":
if len(args) < 2 { return errors.New("usage: option <key> <value>") }
}
return nil
} Try / catch
if err := cfg.Load(crushrcPath); err != nil {
var usageErr error
if strings.Contains(err.Error(), "usage:") {
usageErr = fmt.Errorf("bad crushrc syntax in %s: %w", crushrcPath, err)
}
return usageErr
} Prevention
- Copy builtin invocations from documented examples rather than writing from memory
- After editing crushrc, run crush once to surface syntax errors before relying on the config
- Keep configs under version control so a regression in builtin syntax is easy to diff
- Check HOOKS.md / release notes when upgrading — builtin signatures may change
When it happens
Trigger: Calling a builtin in a .crushrc/.crush.json shell config with missing, extra, or invalid arguments — e.g. 'lsp' without a subcommand (lspAdd), 'hook' with an unknown subcommand (handleHook), 'hook add' with fewer than 2 args (hookAdd), 'hook remove' with missing id (hookRemove), or 'provider'/'model' flags applied with wrong values (applyFlags).
Common situations: Hand-edited crushrc files with typos or omitted arguments; copying an example config that uses an older builtin syntax after a version change; using crushrc builtins outside their expected form (e.g. 'lsp add' with a missing language server path).
Related errors
- failed to load config from paths %v: %w
- task agent not configured
- small model provider not configured
- coder agent not configured
- model provider not configured
AI-assisted analysis of charmbracelet/crush@7944b8e522 (2026-08-29).
Data as JSON: /api/errors/56ebe5a791da64b1.
Report an issue: GitHub.