JuliusBrussee/caveman · error

cave_context_body_missing:${segment.id}

Error message

cave_context_body_missing:${segment.id}

What it means

Thrown while applying transforms to segments matched by a route: the segment exists in the IR but lowered.bodies lacks bytes for its bodyHandle. Same root cause as cave_context_body_missing at prompt assembly, but hit on the transform path.

Source

Thrown at packages/agent/src/runtime.ts:2646

  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,
          safetyClass: segment.safety,
          beforeTokens,
          afterTokens: beforeTokens,
          tokensBasis: "byte_derived",
          recoveryKind: segment.recovery,
          recoveryUsed: false,

View on GitHub (pinned to 27d5a3981a)

Solutions

  1. Re-lower the context to restore a complete bodies map before applying the plan
  2. Ensure any bodies map passed alongside the plan contains all matched segments' handles
  3. Check the segment id in the error to find which body went missing
Defensive patterns

Strategy: validation

Validate before calling

function bodiesCoverTargets(plan: CavePlan, lowered: LoweredContext): boolean {
  return lowered.ir.segments
    .filter((s) => plan.segment_routes.some((r) =>
      r.segment_kind === s.kind && (r.segment_id === undefined || r.segment_id === s.id)))
    .every((s) => lowered.bodies.has(s.bodyHandle));
}

Type guard

function isTransformBodyMissing(e: unknown): string | null {
  if (!(e instanceof Error)) return null;
  const m = /^cave_context_body_missing:(.+)$/.exec(e.message);
  return m ? m[1] : null;
}

Try / catch

if (!bodiesCoverTargets(plan, lowered)) {
  lowered = await lowerContexts(definition);
}
await applyPlan(plan, lowered);

Prevention

When it happens

Trigger: A plan route matches segments whose bodyHandle is absent from lowered.bodies — e.g. bodies stripped after plan matching, a partially serialized LoweredContext, or a custom bodies override missing handles.

Common situations: Serializing/deserializing lowered contexts across process boundaries without the binary bodies; post-processing pipelines that drop bodies; lowering bugs.

Related errors


AI-assisted analysis of JuliusBrussee/caveman@27d5a3981a (2026-08-15). Data as JSON: /api/errors/003b4e472759b440. Report an issue: GitHub.