rohitg00/ai-engineering-from-scratch · error · RangeError

Budget.step requires non-negative tokens and dollars

Error message

Budget.step requires non-negative tokens and dollars

What it means

Budget.step throws a RangeError when passed a negative token count or dollar amount; the agent's per-run budget ledger refuses to record negative usage so it can never be inflated into a refund. Called from the main agent loop for every model turn.

Source

Thrown at phases/19-capstone-projects/01-terminal-native-coding-agent/code/ts/src/plan.ts:41

    const lines = [`GOAL: ${this.goal}`];
    for (const it of this.items) {
      lines.push(`  [${mark[it.status]}] ${it.id}. ${it.description}`);
    }
    return lines.join("\n");
  }
}

export class Budget {
  maxTurns = 50;
  maxTokens = 200_000;
  maxDollars = 5.0;
  turnsUsed = 0;
  tokensUsed = 0;
  dollarsUsed = 0;

  step(tokens: number, dollars: number): void {
    if (tokens < 0 || dollars < 0) {
      throw new RangeError("Budget.step requires non-negative tokens and dollars");
    }
    this.turnsUsed += 1;
    this.tokensUsed += tokens;
    this.dollarsUsed += dollars;
  }

  exceeded(): string | null {
    if (this.turnsUsed >= this.maxTurns) return "turn_limit";
    if (this.tokensUsed >= this.maxTokens) return "token_limit";
    if (this.dollarsUsed >= this.maxDollars) return "dollar_limit";
    return null;
  }

  snapshot(): { turnsUsed: number; tokensUsed: number; dollarsUsed: number } {
    return {
      turnsUsed: this.turnsUsed,
      tokensUsed: this.tokensUsed,
      dollarsUsed: this.dollarsUsed,

View on GitHub (pinned to 39ea8a1c6d)

Solutions

  1. Clamp usage to 0 before calling step: step(Math.max(0,t), Math.max(0,d))
  2. Fix the provider adapter that produced negative usage numbers
  3. Model refunds as a separate ledger method, not negative step()

Example fix

// before
budget.step(usage.tokens - previousTokens, usage.dollars);
// after
budget.step(Math.max(0, usage.tokens - previousTokens), Math.max(0, usage.dollars));
Defensive patterns

Strategy: validation

Validate before calling

if (tokens < 0 || dollars < 0) return; // or clamp 
budget.step(Math.max(0, tokens), Math.max(0, dollars));

Try / catch

try { budget.step(t, d); } catch (e) { if (e instanceof RangeError) logBadUsage(t, d); else throw e; }

Prevention

When it happens

Trigger: Calling step(-100, 0.01), step(500, -0.02), or passing NaN comparisons gone wrong (NaN fails the <0 check but still poisons totals only if negative). Specifically any invocation where tokens<0 or dollars<0.

Common situations: Mocked model clients returning negative usage fields, refund/credit accounting wired directly into step, or computing deltas where a later reading is subtracted from an earlier one.

Related errors


AI-assisted analysis of rohitg00/ai-engineering-from-scratch@39ea8a1c6d (2026-08-26). Data as JSON: /api/errors/897cec579a1e4e19. Report an issue: GitHub.