{"record":{"id":"c5a5bc466b231bbe","repo":"JuliusBrussee/caveman","slug":"cave-plan-route-unmatched","errorCode":"cave_plan_route_unmatched","errorMessage":"cave_plan_route_unmatched:${route.segment_id ?? route.segment_kind}","messagePattern":"cave_plan_route_unmatched:(.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/agent/src/runtime.ts","lineNumber":2640,"sourceCode":"  ].map((name) => `caveman.engine.${name}.v1`));\n  const evaluated: string[] = [];\n  const applied: string[] = [];\n  const failures: string[] = [];\n  const handles = new Set<string>();\n  const trace: MutableTransformTrace[] = [];\n  let recoveryResolved = true;\n  for (const route of plan.segment_routes) {\n    if (!known.has(route.transform_id)) {\n      throw new Error(`cave_unknown_transform:${route.transform_id}`);\n    }\n    const targets = lowered.ir.segments.filter((segment) =>\n      segment.kind === route.segment_kind &&\n      (route.segment_id === undefined || segment.id === route.segment_id));\n    if (targets.length === 0) {\n      if (route.segment_kind === \"history\" || route.segment_kind === \"tool_result\") {\n        continue;\n      }\n      throw new Error(`cave_plan_route_unmatched:${route.segment_id ?? route.segment_kind}`);\n    }\n    evaluated.push(route.transform_id);\n    let routeApplied = false;\n    for (const segment of targets) {\n      const original = lowered.bodies.get(segment.bodyHandle);\n      if (!original) throw new Error(`cave_context_body_missing:${segment.id}`);\n      if (segment.safety !== \"S4\") throw new Error(`cave_transform_safety_mismatch:${segment.id}`);\n      const startedAt = performance.now();\n      // beforeTokens/afterTokens are byte-derived (bytes/4) throughout so a\n      // delta is always within one basis. beforeTokens counts the ORIGINAL\n      // bytes; afterTokens, for an applied transform, counts the FULL provider\n      // body actually sent — wrapper included. segment.tokenCount\n      // is already estimateTokens(original) = bytes/4.\n      const beforeTokens = segment.tokenCount;\n      if (segment.opaque) {\n        trace.push({\n          segmentKind: segment.kind,\n          transformID: route.transform_id,","sourceCodeStart":2622,"sourceCodeEnd":2658,"githubUrl":"https://github.com/JuliusBrussee/caveman/blob/3ee70a102609e550bd2e68004bf5990a9341c851/packages/agent/src/runtime.ts#L2622-L2658","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\nsegment_routes: [{ segment_kind: \"memory\", transform_id: \"...\" }] // agent has no memory segments\n// after\nsegment_routes: [{ segment_kind: \"history\", transform_id: \"...\" }]","handlingStrategy":"validation","validationCode":"for (const route of plan.segment_routes) {\n  const matched = lowered.ir.segments.filter(s => s.kind === route.segment_kind && (route.segment_id === undefined || s.id === route.segment_id));\n  if (matched.length === 0 && route.segment_kind !== \"history\" && route.segment_kind !== \"tool_result\") {\n    console.warn(`route ${route.transform_id} matches nothing (${route.segment_id ?? route.segment_kind})`);\n  }\n}","typeGuard":"function routeMatchesSomething(route: { segment_kind: string; segment_id?: string }, segments: { id: string; kind: string }[]): boolean {\n  return segments.some(s => s.kind === route.segment_kind && (route.segment_id === undefined || s.id === route.segment_id));\n}","tryCatchPattern":"try {\n  await executePlan(plan, lowered);\n} catch (err) {\n  if (err instanceof Error && err.message.startsWith(\"cave_plan_route_unmatched:\")) {\n    const target = err.message.split(\":\").slice(1).join(\":\");\n    // drop the route or retarget it to an existing segment\n  } else throw err;\n}","preventionTips":["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."],"tags":["plan","route","segment","agent-runtime"],"backgroundTag":"resource-not-found","analyzedSha":"3ee70a102609e550bd2e68004bf5990a9341c851","analyzedAt":"2026-09-20T15:53:39.229Z","contentChangedAt":"2026-09-20T15:53:39.229Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}