JuliusBrussee/caveman · error
sink not found in current learn plan
Error message
sink %q not found in current learn plan
What it means
simulateLearnPlan collects the unique sink ids the caller asked to simulate and verifies each one exists in the current learn plan's byID map. If a requested id is absent from the plan, simulation cannot produce a row for it and the whole operation errors out rather than partially simulating. This prevents silently dropping sinks the user explicitly named.
Solutions
- Re-run the learn plan generation and copy the sink id exactly from its output
- Run the unfiltered simulation to list valid sink ids, then retry with one of those
- Check for typos, stale ids, or shell quoting issues in the id argument
Example fix
// before
store.BuildLearnSimulationFiltered(plan, []string{"AGENTS.md:stale-id"})
// after
id := "AGENTS.md:current-id-from-plan" // re-read from plan output
store.BuildLearnSimulationFiltered(plan, []string{id}) Defensive patterns
Strategy: validation
Validate before calling
valid := make(map[string]bool, len(plan.Sinks))
for _, s := range plan.Sinks { valid[s.ID] = true }
for _, id := range requested {
if !valid[id] { return fmt.Errorf("unknown sink id %q; run the plan listing to see valid ids", id) }
} Try / catch
if _, err := store.BuildLearnSimulationFiltered(plan, ids); err != nil {
if strings.Contains(err.Error(), "not found in current learn plan") {
return fmt.Errorf("stale or unknown sink id; regenerate the plan: %w", err)
}
return err
} Prevention
- Always copy sink ids from the current plan output, never from old sessions
- List available sinks first, then select from that list in scripts
- Trim and quote CLI arguments to avoid whitespace mismatches
- Regenerate the plan whenever the scan window or cwd changes
When it happens
Trigger: Calling BuildLearnSimulationFiltered with a sink id (from the filtered CLI selection) that does not match any sink in the plan produced for the current window, e.g. a stale or mistyped id.
Common situations: Copy-pasting a sink id from an older plan/README; ids changed after re-running the learn scan; quoting/whitespace mistakes in CLI arguments; simulating against a different cwd than the plan was built for.
Understand the failure class
Background: "Not found" and "does not exist" errors: why "Task not found", "No such folder", and "Can't find" fire when a lookup comes back empty — this error's family across 14 libraries.
Related errors
- at least one sink id is required
- sink is not a bounded recurring-context or reducible config…
- caveman-cloud MCP changed after setup; refusing destructive…
- caveman-cloud MCP changed during interrupted setup…
- caveman-cloud MCP changed during interrupted removal…
AI-assisted analysis of JuliusBrussee/caveman@3ee70a1026 (2026-09-20).
Data as JSON: /api/errors/ebe7bba02126e742.
Report an issue: GitHub.
Appendix: source
Thrown at proxy/internal/store/learn_simulate.go:68
"Counterfactual covers only content and provider-counted turns actually scanned; it is never projected forward and never converted to dollars.",
"Config and recurring-context methods are summed only under the assumption that they describe disjoint content.",
},
}
byID := make(map[string]Sink, len(plan.Sinks))
for _, sink := range plan.Sinks {
byID[sink.SinkID] = sink
}
unique := map[string]bool{}
for _, id := range sinkIDs {
id = strings.TrimSpace(id)
if id != "" {
unique[id] = true
}
}
ids := make([]string, 0, len(unique))
for id := range unique {
if _, ok := byID[id]; !ok {
return LearnSimulation{}, fmt.Errorf("sink %q not found in current learn plan", id)
}
ids = append(ids, id)
}
if len(ids) == 0 {
return LearnSimulation{}, fmt.Errorf("at least one sink id is required")
}
sort.Strings(ids)
// One owner per config file/family. Prefer broader measured weight, then sink
// id, so overlap refusal is stable regardless of CLI argument order.
owners := map[string]string{}
for _, id := range ids {
sink := byID[id]
key := simulationOverlapKey(sink)
if key == "" {
continue
}
owner, exists := owners[key]View on GitHub (pinned to 3ee70a1026)