JuliusBrussee/caveman · error

unknown usage subcommand

Error message

unknown usage subcommand: %s

What it means

Thrown by runUsage's default branch when the first positional argument is not one of import|link|refresh|unlink. The usage command has exactly those four subcommands; anything else — including empty args, which are caught earlier with a usage line — produces this error.

Solutions

  1. Use one of: `caveman-proxy usage import|link|refresh|unlink <provider>`.
  2. For spend/report views use the `stats` command instead of `usage`.
  3. Run `caveman-proxy --help` (or bare `caveman-proxy usage`) to see the accepted subcommands.

Example fix

// before
caveman-proxy usage status
// after
caveman-proxy stats
Defensive patterns

Strategy: validation

Validate before calling

// shell
case "$SUB" in import|link|refresh|unlink) ;; *) echo "usage subcommands: import|link|refresh|unlink" >&2; exit 2;; esac
caveman-proxy usage "$SUB" "$@"

Prevention

When it happens

Trigger: `caveman-proxy usage show`, `usage importx` (typo), `usage status`, or passing flags where a subcommand is expected.

Common situations: Guessing subcommand names from other CLI tools (e.g. `usage report`, `usage stats` — those belong to other top-level commands); typos; old scripts written before a subcommand was renamed.

Understand the failure class

Background: "Unknown argument", "Invalid value", and "must be one of": invalid CLI argument errors explained — this error's family across 35 libraries.

Related errors


AI-assisted analysis of JuliusBrussee/caveman@3ee70a1026 (2026-09-20). Data as JSON: /api/errors/c5c6c8a9e9f81688. Report an issue: GitHub.

Appendix: source

Thrown at proxy/cmd/caveman-proxy/main.go:655

			fatalJSON(logger, fmt.Errorf("unknown usage link source: %s", provider))
		}
	case "unlink":
		switch provider {
		case "claude", "anthropic":
			if err := spend.DeleteQuotaProvider("anthropic"); err != nil {
				fatalJSON(logger, err)
			}
			printJSON(map[string]any{"unlinked": "claude"})
		case "codex", "openai":
			if err := spend.DeleteQuotaProvider("openai"); err != nil {
				fatalJSON(logger, err)
			}
			printJSON(map[string]any{"unlinked": "codex"})
		default:
			fatalJSON(logger, fmt.Errorf("usage unlink needs claude|codex"))
		}
	default:
		fatalJSON(logger, fmt.Errorf("unknown usage subcommand: %s", sub))
	}
}

func runLearn(logger *slog.Logger, args []string) {
	sub := "scan"
	if len(args) > 0 {
		sub = args[0]
		args = args[1:]
	}
	home := mustHome(logger)
	spend := mustStore(logger, home)
	defer spend.Close()
	cwd, _ := os.Getwd()
	// Empty default defers to the store's normalizeSources default set, so new
	// session sources (gemini/opencode/aider) are scanned without this flag
	// needing to chase the adapter list.
	sources := strings.Split(argFlag(args, "--sources", ""), ",")
	since := argFlag(args, "--since", "30d")

View on GitHub (pinned to 3ee70a1026)