JuliusBrussee/caveman · error

${ledger.exceededCode}

Error message

${ledger.exceededCode}

What it means

Pre-flight reservation for the next model call: for each active ledger, actualUsd + reservedUsd + ceilingUsd must fit under limitUsd, where ceilingUsd is the conservative worst-case cost of the call (byte-derived input ceiling times the catalog's worst rate plus the output allowance - deliberately ~3-4x the likely real cost). When the projection exceeds the limit, the ledger's exceededCode is thrown: 'cave_subagent_cost_budget' for subagent wallets (set at runtime.ts:3977) or 'cave_run_cost_budget_exceeded' for the root maxCostUsd ledger (runtime.ts:794). This is the older error-terminating cap; RunOptions.budget instead stops cleanly between calls with stopReason.

Source

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

): SpendReservation[] {
  if (ledgers.length === 0) return [];
  const outputTokens = Math.min(
    model.maxTokens,
    requestedOutputTokens ?? model.maxTokens,
  );
  const ceilingUsd = catalogSearchCeiling(
    `${model.provider}/${model.id}`,
    model.contextWindow,
    outputTokens,
  );
  if (ceilingUsd === undefined) {
    markSpendIncomplete(ledgers);
    throw new Error("cave_subagent_unpriced_budget");
  }
  for (const ledger of ledgers) {
    if (ledger.incomplete) throw new Error("cave_subagent_spend_evidence_incomplete");
    if (ledger.actualUsd + ledger.reservedUsd + ceilingUsd > ledger.limitUsd) {
      throw new Error(ledger.exceededCode);
    }
  }
  const reservations = ledgers.map((ledger) => ({
    ledger,
    ceilingUsd,
    provider: model.provider,
    model: model.id,
  }));
  for (const { ledger } of reservations) ledger.reservedUsd += ceilingUsd;
  return reservations;
}

function settleProviderSpend(
  reservations: readonly SpendReservation[],
  usage: ValidatedProviderUsage,
): Error | undefined {
  if (reservations.length === 0) return undefined;
  const requested = reservations[0]!;

View on GitHub (pinned to 766dce6b13)

Solutions

  1. Raise the limit (maxCostUsd / subagent cap) so the worst-case ceiling fits with headroom
  2. Lower requestedOutputTokens for the call so the ceiling shrinks
  3. Prefer RunOptions.budget (reserve-and-clamp): it clamps output to what remains and stops between calls with stopReason instead of throwing
  4. Reduce the number of parallel budgeted subagents sharing one cap

Example fix

// before: error-terminating cap sized too tight for worst-case
await run(agent, { maxCostUsd: 1, requestedOutputTokens: 32_000 });

// after: clamping budget, or bigger cap for worst case
await run(agent, { budget: { denomination: 'usd', maxUsd: 1 } }); // clamps + stopReason: budget_exhausted
Defensive patterns

Strategy: try-catch

Try / catch

try {
  return await run(agent, opts);
} catch (error) {
  const msg = error instanceof Error ? error.message : '';
  if (msg === 'cave_subagent_cost_budget' || msg === 'cave_run_cost_budget_exceeded') {
    // worst-case reservation no longer fits: raise the cap or lower output tokens
    return await run(agent, { ...opts, maxCostUsd: opts.maxCostUsd! * 2 });
  }
  throw error;
}

Prevention

When it happens

Trigger: The worst-case ceiling of the next call no longer fits the remaining wallet/root cap; large requestedOutputTokens inflating the ceiling past the limit; several subagents sharing one root maxCostUsd so reservations stack; late-run calls after most of the cap is already actual+reserved spend.

Common situations: maxCostUsd sized to expected average cost rather than worst-case reservation; raising requested output tokens mid-run; fan-out of budgeted subagents against a small root cap; conservative ceiling on huge contexts (byte bound ~3-4x tokens) tripping early.

Related errors


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