Context EngineeringLesson 48 of 87

Contents

Context Engineering

Lesson 48 of 87. Sign in to keep your place.

Module 5 · Cost, caching and latency

How one user spends your whole budget

9 min read

The attack that needs no exploit

Most systems that charge per token have no cap on how many tokens one user can cause you to spend. That is not an unusual oversight; it is the default state of an application built by adding a model call to an existing feature.

The damage does not require malice. Every one of these has happened by accident:

  • A user uploads a 400-page PDF and asks eleven questions about it.
  • An integration retries a failing request every thirty seconds for a weekend.
  • An agent loop with no stopping rule calls a search tool 900 times.
  • A tool returns the entire table because a filter was empty, and the result goes into the window.
  • A document contains 300,000 repeated characters, and the chunker faithfully produces 800 chunks from it.

And deliberately, it is easier still: submit a long input, ask for a long output, repeat. There is no vulnerability to find. The feature works as designed.

The three multipliers

Cost is requests × tokens_per_request × price. An attacker or an accident can inflate all three, and defences have to cover each.

Requests. Rate limiting per user and per key. Ordinary, well-understood, and frequently applied to the HTTP endpoint while the internal agent loop that makes forty model calls per request goes uncounted.

Input tokens. Caps at every entry point. Maximum upload size, maximum extracted characters per document, maximum pages, maximum tool result size, maximum history retained. Each cap belongs at the boundary where the data arrives, not in the assembler at the end: by then you have already parsed and embedded it.

Output tokens. A hard max_tokens on every call. Also a defence against a prompt-injection payload that asks the model to produce as much text as possible, which is a cheap way to run up a bill through a document you did not write.

The budget that actually works

Rate limits do not bound cost, because one request can be a thousand times more expensive than another. Bound the thing you are billed for:

Python
spent = redis.incrby(f"tok:{user_id}:{today}", tokens_used)
if spent > DAILY_TOKEN_BUDGET[user.tier]:
    raise BudgetExceeded(user_id, spent)

Count after each call using the billed usage figures, and check before the next one. Set the budget per tier from the actual distribution of legitimate use (the 99th percentile of your real users, with headroom) rather than from a guess. And define what happens on exhaustion: a clear message, a degraded free path, or a queue, but not a silent failure.

The same counter, aggregated, gives you a global daily ceiling and a kill switch. A ceiling that stops the whole feature is unpleasant. It is much less unpleasant than a bill that arrives after the weekend.

Bounding the loops

Agents multiply everything, because one user action becomes an unknown number of calls. Three caps, all enforced in code rather than in instructions:

  1. Maximum iterations per task. A hard integer.
  2. Maximum accumulated tokens per task, checked between steps and abandoning the task when exceeded.
  3. Maximum tool result size, truncated at the tool boundary with a note saying it was truncated.

The third is the one that prevents the specific failure from module one, where an oversized tool result pushed the instructions out of the window. It is a cost defence and a correctness defence at the same time.

Watch the tail, not the mean

The metric that catches this early is the 99th percentile of tokens per request, and the maximum, per user and per endpoint. A mean of 6,000 tokens with a maximum of 900,000 is a system with an unbounded path in it, and the mean will keep looking healthy right up until the day somebody finds that path.

Alert on the maximum, not the average. Then find the request behind it and read what was in the window.

The uncomfortable trade

Every cap here degrades some legitimate use. A researcher wants to ask eleven questions about a 400-page document. A caps-everywhere system tells them no.

The honest resolution is tiering rather than pretending the trade does not exist: generous limits for accounts you can bill or verify, tight limits for anonymous and free use, and an explicit path to request more. State the limits in the product rather than failing mysteriously at the boundary: a user who knows there is a 50-page limit uploads the right chapter; a user who does not is a support ticket.

What you should not do is leave it unbounded because bounding it is awkward. The awkwardness is finite and the bill is not.

Every lesson here is open. Your progress is recorded once you pass the Data, SQL and Getting to the Answer exam.

Check your understanding

A team adds a strict per-user request rate limit, yet a single account still produces a very large bill. What did the rate limit fail to bound?

Pick the one you would defend. Your answer stays private.

Next lesson · 8 minEvery turn carries all the previous ones

Stuck on something?Ask Sayali, your AI tutor

How to cite: Addaly (2026) ‘How one user spends your whole budget’, in Context Engineering. Edited by M. Mun. Addaly. Available at: https://addaly.com/learn/context-engineering/denial-of-wallet-and-context-bombs (Accessed: 13 October 2026).