charmbracelet/crush · error
%s: --%s has unknown flag kind
Error message
%s: --%s has unknown flag kind
What it means
This error is the default branch of parseFlagValue's kind switch: it fires when a flagSpec has a flagKind value the parser does not recognize. This is an internal programming error, not a user-input error — it means a developer added a new flagKind constant to the flagKind enum but did not add a corresponding case in parseFlagValue. Because flagKind is an int enum with no exhaustiveness check, the compiler cannot catch the missing case.
Source
Thrown at internal/shellconfig/flags.go:178
var object map[string]any
if err := json.Unmarshal([]byte(v), &object); err != nil || object == nil {
return nil, 0, fmt.Errorf("%s: --%s expects a JSON object, got %q", args[0], name, v)
}
return object, i + 2, nil
case flagJSONAny:
v, err := nextArg(args, i, name)
if err != nil {
return nil, 0, err
}
var parsed any
if err := json.Unmarshal([]byte(v), &parsed); err != nil {
return nil, 0, fmt.Errorf("%s: --%s expects valid JSON, got %q: %s", args[0], name, v, err)
}
return parsed, i + 2, nil
default:
return nil, 0, fmt.Errorf("%s: --%s has unknown flag kind", args[0], name)
}
}
// nextArg returns args[i+1], erroring if the flag is missing its value.
func nextArg(args []string, i int, flag string) (string, error) {
if i+1 >= len(args) {
return "", fmt.Errorf("%s: --%s requires a value", args[0], flag)
}
return args[i+1], nil
}
// storeFlag writes a parsed value into target according to spec.op.
func storeFlag(target map[string]any, spec flagSpec, val any) {
switch spec.op {
case opSet:
target[spec.jsonKey] = val
case opAppend:
arr, _ := target[spec.jsonKey].([]any)View on GitHub (pinned to 7944b8e522)
Solutions
- Add a case for the new flagKind in parseFlagValue's switch in internal/shellconfig/flags.go.
- Check the flagSpec tables in the shell builtins for a spec whose kind was added without parser support.
- As a library change, replace the int enum default with a compile-time exhaustiveness check or a unit test that iterates all flagKind values.
- File/fix as a bug in crush; pin or downgrade the version if hit from an unmodified release build.
Example fix
// before (new kind with no case)
const flagStringSlice flagKind = iota_last
// after
case flagStringSlice:
v, err := nextArg(args, i, name)
if err != nil { return nil, 0, err }
return strings.Split(v, ","), i + 2, nil Defensive patterns
Strategy: type-guard
Validate before calling
// Compile-time guard in the library: ensure every flagKind is accounted for
var _ = map[flagKind]struct{}{
flagString: {}, flagBool: {}, flagBoolTrue: {}, flagInt: {},
flagFloat: {}, flagKeyValue: {}, flagJSONObject: {}, flagJSONAny: {},
} // extend this map when adding a new kind Type guard
func isKnownFlagKind(k flagKind) bool {
switch k {
case flagString, flagBool, flagBoolTrue, flagInt, flagFloat,
flagKeyValue, flagJSONObject, flagJSONAny:
return true
}
return false
} Try / catch
if err := applyFlags(specs, args, start, target, cmd, stderr); err != nil {
if strings.Contains(err.Error(), "has unknown flag kind") {
// library bug: report to crush maintainers, do not retry
return fmt.Errorf("internal error in shellconfig flags: %w", err)
}
return err
} Prevention
- When adding a flagKind constant, always add the matching case in parseFlagValue in the same PR.
- Add a unit test that iterates all flagKind values and asserts no 'unknown flag kind' error.
- Consider replacing the default branch with a panic during development to fail loudly in tests.
- Keep the enum and the switch adjacent in flags.go to reduce drift.
When it happens
Trigger: A developer defines a new flagKind constant (beyond flagJSONAny) in internal/shellconfig/flags.go and registers a flagSpec using it, then any builtin invocation that reaches that flag via applyFlags hits the default branch. Not reachable by end-user CLI input alone.
Common situations: Adding a flag kind (e.g. a future flagStringSlice) and forgetting the switch case; refactoring the enum and reordering iota constants so an unhandled value appears; merging a partial PR that adds the constant but not the parser case.
Related errors
- unsupported by the running server
- clipboard operations are not supported on this platform
- clipboard is empty or holds an unsupported format
- Crush crashed. If metrics are enabled, we were notified abou
- empty providers list from catwalk
AI-assisted analysis of charmbracelet/crush@7944b8e522 (2026-08-29).
Data as JSON: /api/errors/4ca409dc41c1f8bf.
Report an issue: GitHub.