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

  1. Re-run the learn plan generation and copy the sink id exactly from its output
  2. Run the unfiltered simulation to list valid sink ids, then retry with one of those
  3. 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

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


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)