JuliusBrussee/caveman · error
sink is not a bounded recurring-context or reducible config…
Error message
sink %q is not a bounded recurring-context or reducible config sink
What it means
simulateLearnPlan can only compute arithmetic for sinks that are either recurring-context sinks with a bounded bytes4 estimate or reducible config sinks (isSimulatableConfigSink). Any other sink kind hits the final else branch and errors out, because no bounded estimation method exists for it. This is a deliberate fail-closed guard against extrapolating unbounded context growth.
Solutions
- Restrict the simulation selection to sinks the plan marks as recurring-context or reducible config sinks
- Check the plan row's method/basis to see which sinks support arithmetic before selecting them
- If the sink should be simulatable, ensure the scan classified it as bounded (or add support upstream)
Example fix
// before
store.BuildLearnSimulationFiltered(plan, allSinkIDs)
// after
var simulatable []string
for _, id := range allSinkIDs {
if isSimulatable(plan.Sinks[id]) {
simulatable = append(simulatable, id)
}
}
store.BuildLearnSimulationFiltered(plan, simulatable) Defensive patterns
Strategy: type-guard
Validate before calling
func isSimulatable(s PlanSink) bool {
return s.Kind == "recurring-context" || isSimulatableConfigSink(s)
} Type guard
func canSimulate(id string, byID map[string]PlanSink) bool {
s, ok := byID[id]
if !ok { return false }
return s.Basis == "bytes4_estimate" || isSimulatableConfigSink(s)
} Prevention
- Only select sinks whose plan row shows a bounded basis (bytes4_estimate) or a config-reducible method
- Check the plan's method/caveat fields before bulk-selecting all sinks
- Treat establish-only or unbounded sinks as display-only in tooling
- Keep sink-kind classification and simulation support in sync when adding new kinds
When it happens
Trigger: Passing a sink id to BuildLearnSimulationFiltered whose sink kind is neither a bounded recurring-context sink (bytes4_estimate path) nor a simulatable config sink, e.g. a recall-style or unbounded-growth sink.
Common situations: Selecting all plan sinks in a bulk simulation; a new sink kind was added to the plan but simulation support was not; confusing an establish-only sink with a simulatable one.
Understand the failure class
Background: UnsupportedOperationException and "is not supported" errors: when a library deliberately refuses a call — this error's family across 30 libraries.
Related errors
- at least one sink id is required
- sink not found in current learn plan
- MCP changes require the ownership transaction
- caveman-cloud MCP changed after setup; refusing destructive…
- caveman-cloud MCP changed during interrupted setup…
AI-assisted analysis of JuliusBrussee/caveman@3ee70a1026 (2026-09-20).
Data as JSON: /api/errors/68f90181ffe8d718.
Report an issue: GitHub.
Appendix: source
Thrown at proxy/internal/store/learn_simulate.go:107
owners[key] = id
}
}
var total int64
hasSummable := false
totalValid := true
for _, id := range ids {
sink := byID[id]
row := LearnSimulationRow{SinkID: id}
if strings.HasPrefix(id, "recurring_context:repaste:") {
row.Method = "occurrence_sum"
row.Basis = "bytes4_estimate"
row.Caveats = []string{"Arithmetic retains one largest observed occurrence and charges one standard pointer for every observed occurrence; recall cost is unavailable here, so real apply still requires the net-token-negative gate."}
} else if isSimulatableConfigSink(sink) {
row.Method = "per_turn_times_observed_turns"
row.Caveats = []string{fmt.Sprintf("Would not have been established across the %d provider-counted turns actually scanned; no future turn is included.", observedTurns)}
} else {
return LearnSimulation{}, fmt.Errorf("sink %q is not a bounded recurring-context or reducible config sink", id)
}
if key := simulationOverlapKey(sink); key != "" && owners[key] != id {
row.Caveats = append(row.Caveats, fmt.Sprintf("Excluded from arithmetic because it overlaps %s on %s; counting both would double-count.", owners[key], key))
out.Rows = append(out.Rows, row)
continue
}
var skipped int64
if row.Method == "occurrence_sum" {
totalTokens, totalOK := int64Evidence(sink.Evidence["occurrence_tokens_total"])
largest, largestOK := int64Evidence(sink.Evidence["block_tokens"])
occurrences, occurrencesOK := int64Evidence(sink.Evidence["occurrences_total"])
if !totalOK || !largestOK || !occurrencesOK || occurrences <= 0 {
row.Caveats = append(row.Caveats, "Required occurrence totals were unavailable; no arithmetic emitted.")
out.Rows = append(out.Rows, row)
continue
}View on GitHub (pinned to 3ee70a1026)