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

  1. Read the usage message printed to stderr — it states the exact expected syntax for the builtin that failed
  2. Open the crushrc file and fix the offending builtin invocation to match the documented syntax
  3. Check HOOKS.md / internal/shellconfig docs for the current builtin signatures (syntax may have changed between versions)
  4. 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

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


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