JuliusBrussee/caveman · error
unknown learn subcommand: %s
Error message
unknown learn subcommand: %s
What it means
The `learn` command dispatcher hit a subcommand token outside its known set (scan is the default, plus apply). fatalJSON echoes the token and exits 1. Note learn defaults to scan when run with no args, so this only fires on an explicit unknown token.
Source
Thrown at proxy/cmd/caveman-proxy/main.go:603
fatalJSON(logger, err)
}
if hasArg(args, "--json") {
printJSON(plan)
return
}
printJSON(map[string]any{"report": out, "sidecar": sidecar, "cave_score": plan.CaveScore.Score, "basis": plan.Basis, "sinks": len(plan.Sinks)})
case "apply":
sinkID := firstPositional(args)
if sinkID == "" {
fatalJSON(logger, fmt.Errorf("usage: caveman-proxy learn apply <sink_id> [--dry-run]"))
}
plan, err := spend.BuildLearnPlan(cwd, sources, since)
if err != nil {
fatalJSON(logger, err)
}
applyLearnSink(logger, home, sinkID, hasArg(args, "--dry-run"), plan)
default:
fatalJSON(logger, fmt.Errorf("unknown learn subcommand: %s", sub))
}
}
func learnRetroOptions(args []string) store.RetroOptions {
behaviorBudget, _ := strconv.Atoi(argFlag(args, "--behavior-budget-ms", "0"))
retroBudget, _ := strconv.Atoi(argFlag(args, "--retro-budget-ms", "0"))
return store.RetroOptions{
Enabled: hasArg(args, "--retro"),
BehaviorBudgetMS: behaviorBudget,
BudgetMS: retroBudget,
}
}
// applyLearnSink materializes a candidate edit for a reducible sink under
// ~/.caveman/candidates/ and returns it. It never edits user config files itself —
// the analyzer is read-only; the consent-gated editing skill performs real edits.
func applyLearnSink(logger *slog.Logger, home, sinkID string, dryRun bool, plan store.LearnPlan) {
var target *store.SinkView on GitHub (pinned to 27d5a3981a)
Solutions
- Use scan (or no subcommand) to analyze, apply <sink_id> to materialize a candidate
- Check help output for the exact verb set of your binary
- For the cave-score report, read the scan JSON rather than a separate verb
Example fix
# before caveman-proxy learn report # after caveman-proxy learn scan --json
Defensive patterns
Strategy: validation
Validate before calling
case "$SUB" in ""|scan|apply) caveman-proxy learn $SUB "$@";; *) echo "unknown learn subcommand: $SUB" >&2;; esac
Prevention
- learn defaults to scan with no args — use that for analysis
- apply <sink_id> is the only other verb; reports come from scan output
- Verify the verb set against your binary version before scripting
When it happens
Trigger: `caveman-proxy learn report`, `learn list`, or a typo like `learn aply`. The switch only has scan/apply arms.
Common situations: Expecting report verbs under learn (the scan output is the report); scripts iterating assumed verbs; muscle-memory from other agent CLIs.
Related errors
- usage: caveman-proxy trial promote <optimizer_id> --trial-id
- unknown trial subcommand: %s
- usage: caveman-proxy usage import|link|refresh|unlink <provi
- usage: caveman-proxy usage import codex|claude
- unknown usage source: %s
AI-assisted analysis of JuliusBrussee/caveman@27d5a3981a (2026-08-15).
Data as JSON: /api/errors/871e58d23d40414f.
Report an issue: GitHub.