JuliusBrussee/caveman · error · Error

cave_transform_trace_basis_mixed

cave_transform_trace_basis_mixed

Error message

cave_transform_trace_basis_mixed

What it means

A turn bill sums before/after token counts over the transforms applied that turn, and a token delta is only meaningful within one measurement basis — `byte_derived` (local estimate) or `engine_counted` (loopback engine's own count). `turnBill` refuses to blend the two into a single reduction figure and throws this invariant error instead of publishing a dishonest number.

Source

Thrown at packages/agent/src/code.ts:785

  // missing runtime does.
  if (result.mode === "observe-only") degrade(session);
  const record: TurnRecord = {
    input,
    text: result.text,
    toolCalls: result.toolCalls,
    bill: turnBill(session.turns.length + 1, result),
    degraded: startedOptimized && session.mode === "observe-only",
  };
  session.turns.push(record);
  return record;
}

function turnBill(turn: number, result: RunResult): TurnBill {
  const applied = result.transformTrace.filter((item) => item.outcome === "applied");
  // A token delta is only meaningful within one basis; refuse to blend
  // byte-derived and engine-counted figures into a single reduction.
  const bases = new Set(applied.map((item) => item.tokensBasis));
  if (bases.size > 1) throw new Error("cave_transform_trace_basis_mixed");
  const tokensBasis: TransformTrace["tokensBasis"] = applied[0]?.tokensBasis ?? "byte_derived";
  const before = applied.reduce((sum, item) => sum + item.beforeTokens, 0);
  const after = applied.reduce((sum, item) => sum + item.afterTokens, 0);
  return {
    turn,
    mode: result.mode,
    provider: result.provider,
    model: result.model,
    contextBill: result.contextBill,
    transformedTokensBefore: before,
    transformedTokensAfter: after,
    // The reduction is the delta of what was ACTUALLY SENT, so a barely-
    // compressible turn whose wrapper cost more than it saved reports zero
    // rather than a phantom positive; the raw before/after stay exposed.
    tokensSavedInferred: Math.max(0, before - after),
    tokensBasis,
    transformIDs: result.transformIDs,
    transformFailures: result.transformFailures,

View on GitHub (pinned to 766dce6b13)

Solutions

  1. Keep engine configuration uniform for the whole session: either run with the loopback engine the entire time or fully cold (observe-only), not a mix.
  2. Restart the session (`startCodingSession` again) after changing engine availability so the route pins one basis.
  3. If it reproduces with a stable configuration, capture `RunResult.transformTrace` and file an issue — per-turn basis uniformity is an internal contract.
Defensive patterns

Strategy: try-catch

Validate before calling

function hasMixedBases(result: RunResult): boolean {
  const bases = new Set(
    result.transformTrace
      .filter((item) => item.outcome === "applied")
      .map((item) => item.tokensBasis),
  );
  return bases.size > 1;
}

Try / catch

try {
  const record = await runCodingTurn(session, input);
} catch (error) {
  if (error instanceof Error && error.message === "cave_transform_trace_basis_mixed") {
    // billing invariant broke: keep the session, mark the token bill unavailable
    session.notices.push("token bill unavailable: mixed token bases this turn");
    return;
  }
  throw error;
}

Prevention

When it happens

Trigger: A single run's `RunResult.transformTrace` contains applied entries with different `tokensBasis` values — typically part of the turn's transforms got engine token counts while another transform route (or a transform-failure recovery path) reported byte-derived figures. Surfaces from `runCodingTurn` when the turn record is billed.

Common situations: Engine/loopback runtime becomes available or drops mid-session so routes flip between counted and estimated; version skew between gateway and engine; usually indicates a framework bug worth reporting rather than a user config mistake.

Related errors


AI-assisted analysis of JuliusBrussee/caveman@766dce6b13 (2026-08-18). Data as JSON: /api/errors/7eb328c75c5ba03e. Report an issue: GitHub.