Tvijo wiki
Catalog

Topics

A daily AI budget that fails closed: caps, session ids and a gap we found

One unattended job once made about 118,000 model calls in a day for under eighteen dollars, and we could not tell which job it was. Two rules would have stopped it: a daily cap that refuses when unset, and a session id on every call.

🎯 For A developer who runs AI calls in loops, cron jobs or background workers

πŸ“š You will learn
  • What a runaway AI job looks like in your own numbers
  • Why a cap must refuse when it is missing, and why every call needs a label
  • How to check your own budget helper for the gap we found in ours

πŸ”₯ What happened

On 26 June 2026 our trace database recorded 117,917 model calls for one day at a cost of $17.88. The day before had 1,809 calls and $2.14. That is about 65 times the volume. Of the 117,917 calls, 116,382 carried no session id, so the trace could not say which job made them. At first the blame went to the wrong job. Only the per-day, per-session numbers pointed to the real one, an automatic translation loop. Our incident note puts the damage at roughly seventeen and a half dollars. The trace for that day shows $17.88, which also includes the ordinary traffic of the day.

The money was small. The lesson was not: a loop that calls a model for every item, with no cap, scales to whatever the input has.

🧱 Two rules

Rule one: every automatic path has a daily cap, and a missing cap means refuse. The cap is read from an explicit setting. If the setting is empty, zero or negative, the job must not start. The opposite default, "no setting, no limit", turns a forgotten line of configuration into unlimited spending.

Rule two: every call carries a session id. The id names the job, not the person. Without it, the first question after an alarm, "who did this?", has no answer. We do not commit automation that calls a model without one.

πŸ”Ž The gap we found while writing this

Our helper that tracks a daily budget for background jobs reads the limit from an environment variable. We ran it on 3 November 2026 with the variable empty. The limit came out as 0.0 and the call to ask whether spending is allowed returned yes. In other words, in that helper a missing cap means no cap, which is the opposite of rule one. Callers that set a positive limit are fine. A caller that forgets the variable is not protected.

Our metered transport, the lane that pays providers, behaves correctly: it refuses with "explicit daily cap required" when the cap is missing. The two helpers disagree, and we have recorded the helper as a defect to fix. The lesson is to test your own guard with the setting removed.

πŸ“ˆ How big is the unlabelled share

Since 2026-04-24 our trace database holds 777,526 calls costing $164.31 in total. Of these, 125,541 calls, 16.1 percent, have no session id, and they cost $24.57, 15.0 percent of the total. Most of that comes from the one day above. Unlabelled spending is not a rounding error.

πŸ› οΈ Practice: close both gaps

  1. List every place that calls a model in a loop, on a schedule or in the background.
  2. For each, find the daily cap setting. If there is none, add one. If the code treats empty as unlimited, change it to refuse.
  3. Write a test that starts the job with the setting removed and asserts that no call is made.
  4. Add a session id to every call. Make the wrapper raise an error when it is missing.
  5. Query your traces for calls with no id, per day. Make that query an alarm.
  6. Set the alarm threshold from your normal day, for example three times the median of the last fourteen days.

βœ… Check yourself

  • Unset the cap for one job in a test environment. The job refuses and says why.
  • Pick any call from last week. You can name the job that made it.
  • The count of calls without an id is zero for the last seven days, or you can explain each one.
  • You can read yesterday's spend per job from one query.

🚧 When this does NOT work

  • A cap that is too high. A cap of a hundred dollars protects nothing on a ten-dollar job. Set it from the real need.
  • Spend that arrives late. Some providers report cost after the call. A cap based on reported spend can be overshot by what is in flight. Reserve before you call.
  • Shared keys. If five jobs share one key, the provider's bill cannot separate them. Your session id has to.
  • Retries. A retry loop can multiply spend. Count every attempt against the cap.
  • Free models. Free tiers have rate limits and may disappear. A cap of zero dollars still needs a call limit.

πŸ“Š Evidence

What we measured Calls and cost per day, and calls without a session id, in our trace database; behaviour of the budget helper with an empty limit
When 3 November 2026, 22:06 UTC
Artifact The LLM trace database (777,526 calls since 2026-04-24); the day of 26 June 2026 and a direct run of the helper
Limit One incident and one helper. The trace shows volume and cost, not which job it was; the job is named in our incident note

➑️ Next

Write the limits down with Costs, budgets and limits, and decide what happens when a job goes wrong in An AI incident playbook for a small team.

Sources

πŸ”— Article connections

Open the full knowledge graph β†—

Published article links. Select a node to open its article at the heading.

100%
β†’ Linked articles← Linked fromβ‹― Same topic
Article links Β· keyboard / text view

πŸ’¬ Comments

Comments are public and moderated. Do not post personal data.
  • No comments yet. Be the first!

Next steps