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.Sink

View on GitHub (pinned to 27d5a3981a)

Solutions

  1. Use scan (or no subcommand) to analyze, apply <sink_id> to materialize a candidate
  2. Check help output for the exact verb set of your binary
  3. 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

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


AI-assisted analysis of JuliusBrussee/caveman@27d5a3981a (2026-08-15). Data as JSON: /api/errors/871e58d23d40414f. Report an issue: GitHub.