JuliusBrussee/caveman · error
cave_plan_route_unmatched
cave_plan_route_unmatched
Error message
cave_plan_route_unmatched:${route.segment_id ?? route.segment_kind} What it means
Thrown when a compaction plan route matches no segments in the lowered IR. Routes targeting 'history' or 'tool_result' kinds silently skip (continue), but any other route whose segment_kind (or segment_id) matches zero segments is a hard error — the plan promised to touch context that does not exist.
Solutions
- Remove the route from the plan, or change its segment_kind/segment_id to one that exists in the IR.
- Enumerate lowered.ir.segments (id/kind) and rewrite the plan to target actual segments.
- Make plan generation derive routes from the definition's declared contexts instead of using a static plan.
- If the route is intentionally optional, target only history/tool_result kinds, which the runtime skips when unmatched.
Example fix
// before
segment_routes: [{ segment_kind: "memory", transform_id: "..." }] // agent has no memory segments
// after
segment_routes: [{ segment_kind: "history", transform_id: "..." }] Defensive patterns
Strategy: validation
Validate before calling
for (const route of plan.segment_routes) {
const matched = lowered.ir.segments.filter(s => s.kind === route.segment_kind && (route.segment_id === undefined || s.id === route.segment_id));
if (matched.length === 0 && route.segment_kind !== "history" && route.segment_kind !== "tool_result") {
console.warn(`route ${route.transform_id} matches nothing (${route.segment_id ?? route.segment_kind})`);
}
} Type guard
function routeMatchesSomething(route: { segment_kind: string; segment_id?: string }, segments: { id: string; kind: string }[]): boolean {
return segments.some(s => s.kind === route.segment_kind && (route.segment_id === undefined || s.id === route.segment_id));
} Try / catch
try {
await executePlan(plan, lowered);
} catch (err) {
if (err instanceof Error && err.message.startsWith("cave_plan_route_unmatched:")) {
const target = err.message.split(":").slice(1).join(":");
// drop the route or retarget it to an existing segment
} else throw err;
} Prevention
- Derive plan routes from the agent definition's actual contexts/kinds, not static templates.
- Prefer history/tool_result routes for generic plans since the runtime skips them when unmatched.
- Diff plans against the segment list after any definition change.
- Give optional routes explicit 'optional' handling in your plan generator.
When it happens
Trigger: A plan route with segment_kind other than history/tool_result (e.g. 'context', 'memory') where the IR contains no segments of that kind, or a route with a specific segment_id that doesn't exist in lowered.ir.segments.
Common situations: A generic plan applied to an agent definition that lacks that segment type (no memory configured, say); a plan referencing a context id that was renamed or removed; running the same plan across different agents with different context shapes.
Understand the failure class
Background: 'Could not be found', 'does not exist', 'not found in database': the resource-not-found family when an ID, slug, key, or URI lookup comes back empty — this error's family across 20 libraries.
Related errors
- cave_context_body_missing
- cave_context_segment_missing
- cave_transform_safety_mismatch
- cave_unknown_transform
- cave_budget_conflicting_cap
AI-assisted analysis of JuliusBrussee/caveman@3ee70a1026 (2026-09-20).
Data as JSON: /api/errors/c5a5bc466b231bbe.
Report an issue: GitHub.
Appendix: source
Thrown at packages/agent/src/runtime.ts:2640
].map((name) => `caveman.engine.${name}.v1`));
const evaluated: string[] = [];
const applied: string[] = [];
const failures: string[] = [];
const handles = new Set<string>();
const trace: MutableTransformTrace[] = [];
let recoveryResolved = true;
for (const route of plan.segment_routes) {
if (!known.has(route.transform_id)) {
throw new Error(`cave_unknown_transform:${route.transform_id}`);
}
const targets = lowered.ir.segments.filter((segment) =>
segment.kind === route.segment_kind &&
(route.segment_id === undefined || segment.id === route.segment_id));
if (targets.length === 0) {
if (route.segment_kind === "history" || route.segment_kind === "tool_result") {
continue;
}
throw new Error(`cave_plan_route_unmatched:${route.segment_id ?? route.segment_kind}`);
}
evaluated.push(route.transform_id);
let routeApplied = false;
for (const segment of targets) {
const original = lowered.bodies.get(segment.bodyHandle);
if (!original) throw new Error(`cave_context_body_missing:${segment.id}`);
if (segment.safety !== "S4") throw new Error(`cave_transform_safety_mismatch:${segment.id}`);
const startedAt = performance.now();
// beforeTokens/afterTokens are byte-derived (bytes/4) throughout so a
// delta is always within one basis. beforeTokens counts the ORIGINAL
// bytes; afterTokens, for an applied transform, counts the FULL provider
// body actually sent — wrapper included. segment.tokenCount
// is already estimateTokens(original) = bytes/4.
const beforeTokens = segment.tokenCount;
if (segment.opaque) {
trace.push({
segmentKind: segment.kind,
transformID: route.transform_id,View on GitHub (pinned to 3ee70a1026)