Loop Timeouts and Wall-Clock Limits
Picture a user waiting 90 seconds while your agent grinds through step 20. A well-designed agent loop timeout ends the wait gracefully instead of letting runs drag forever. Here's how to build one.
import time
def agent_loop(state, time_budget_s=30, max_steps=20):
deadline = time.monotonic() + time_budget_s
for _ in range(max_steps):
if time.monotonic() >= deadline:
return wrap_up(state, reason="time budget reached")
remaining = deadline - time.monotonic()
action = model_decide(state, tools, time_left=remaining)
if action.name == "finish":
return action.args["answer"]
state = update_state(state, action, run_tool(action))
return force_answer(state)Picture this: a user submits a question, and your agent decides it needs to be thorough. Ninety seconds later they're still staring at a spinner while the agent grinds through its twentieth step, chasing a level of completeness nobody asked for. By the time it answers, the user has given up and refreshed the page. The run cost you money and delivered nothing, because there was no agent loop timeout to say "that's enough, answer with what you have."
Step ceilings alone don't solve this — a step can take one second or thirty, so twelve steps might finish fast or run for minutes. What you need is a wall-clock budget. This guide shows how to build one that ends runs gracefully instead of abruptly.
Quick-Start (Copy This Right Now)
Here's a loop with a wall-clock budget you can drop in today.
[object Object], time
,[object Object], ,[object Object],(,[object Object],):
deadline = time.monotonic() + time_budget_s
,[object Object], _ ,[object Object], ,[object Object],(max_steps):
,[object Object], time.monotonic() >= deadline:
,[object Object], wrap_up(state, reason=,[object Object],)
remaining = deadline - time.monotonic()
action = model_decide(state, tools, time_left=remaining)
,[object Object], action.name == ,[object Object],:
,[object Object], action.args[,[object Object],]
state = update_state(state, action, run_tool(action))
,[object Object], force_answer(state)What this does: It sets a monotonic deadline, checks it before every step, and when time runs out it calls a graceful wrap-up instead of continuing — turning an open-ended run into one bounded by a real wall-clock agent loop timeout.
Understanding the Variables
Three choices shape how a timeout behaves.
The budget itself is the obvious one, but pick it from the user's tolerance, not the task's appetite. An interactive chat agent might get 15 seconds; a background batch agent might get 10 minutes. The right budget is "how long will the person or system on the other end actually wait?" — not "how long might the agent want?"
The check granularity matters more than people expect. Checking the deadline only between steps means a single slow step can blow far past the budget — if you're at 29 seconds and take a 40-second tool call, you finish at 69. For tight budgets, you need timeouts on the individual tool calls too, not just between them, so no single operation can overrun the whole budget.
The wrap-up behavior is what separates a good timeout from a bad one. A crude timeout just kills the run and returns nothing. A good one uses its last moments to synthesize the best answer possible from what it has gathered so far. The difference is a user getting "here's what I found, though I couldn't fully confirm X" versus a blank error.
It's worth distinguishing an agent loop timeout from the step ceiling you probably already have, because teams often assume one covers the other. A step ceiling caps how many iterations run; a wall-clock budget caps how long they take. They fail to substitute for each other in both directions. Twelve steps can finish in three seconds or run for four minutes depending on tool latency, so a step ceiling gives you no real latency guarantee. And a pure time budget with no step ceiling can let a fast-but-looping agent burn a thousand cheap steps inside its window. You want both: the step ceiling bounds work, the time budget bounds waiting, and only together do they bound the user's actual experience.
⚡ Pro tip: Set your time budget and step ceiling so that in normal operation, neither fires — both should be safety nets, not the primary control. If your agent routinely hits the timeout or the step cap to finish, those limits are masking an efficiency problem, and you're shipping truncated answers as a matter of course rather than as a rare exception.
⚡ Pro tip: Use
time.monotonic()time.time()Step-by-Step: Building a Graceful Timeout
Start by making the budget visible to the model. An agent that knows it has eight seconds left behaves differently from one that thinks it has forever — it stops exploring and starts converging.
[object Object], ,[object Object],(,[object Object],):
urgency = ,[object Object],
,[object Object], time_left < ,[object Object],:
urgency = (,[object Object],
,[object Object],
,[object Object],)
,[object Object], base_prompt(state) + urgencyWhat this does: It injects a shrinking time budget into the model's context and, when time runs low, explicitly instructs it to stop exploring and converge — so the agent spends its final steps closing out rather than opening new threads.
Next, build the wrap-up path. When the deadline hits, don't just return an error — give the model one final call to produce the best answer it can, clearly marked as time-limited.
[object Object], ,[object Object],(,[object Object],):
answer = model_synthesize_best_effort(state)
,[object Object], {
,[object Object],: answer,
,[object Object],: ,[object Object],,
,[object Object],: ,[object Object],
,[object Object],,
}What this does: It spends the run's final moment turning whatever the agent has gathered into a usable, honestly-labeled partial answer, so a timeout degrades to a caveated result rather than a hard failure.
Finally, add per-tool timeouts so no single call can eat the whole budget, and make a tool timeout a recoverable event the loop can route around rather than a crash.
⚡ Pro tip: Reserve a slice of your budget for wrap-up. If your total budget is 30 seconds, trigger the "converge now" signal at 24 and hard-stop new steps at 27, leaving 3 seconds to synthesize. An agent that uses every last millisecond exploring has no time left to actually write the answer, which defeats the point.
Pro-Level Variations
For a customer-facing chat agent, use a tiered budget: a soft limit where the agent starts converging and a hard limit where it must answer, so most runs finish naturally before the soft limit and only stragglers feel the pressure.
For a background research agent, the budget can be generous but should still exist — even a ten-minute job needs a ceiling, or one pathological run ties up a worker indefinitely. A data-engineering lead I worked with added a wall-clock cap to their overnight agent after a single run hung for six hours and blocked the whole queue.
For a multi-agent system, budget at the system level and allocate slices to sub-agents, so one slow worker can't consume the time meant for the others. The coordinator hands each worker a shrinking share of the remaining budget. This prevents a subtle failure where an early worker runs long, leaving later workers with almost no time and forcing them into low-quality wrap-ups — the coordinator has to protect each worker's share, not just cap the total, or the last worker in line always gets starved.
Troubleshooting Common Issues
If your agent regularly hits the timeout, don't just raise the budget reflexively — first check whether it's genuinely doing useful work or thrashing. A timeout firing often is usually a symptom of a loop-efficiency problem upstream, and raising the budget just hides it while raising cost.
If timed-out answers are low quality, your wrap-up is starved. Either reserve more budget for synthesis or make the "converge now" signal fire earlier, so the agent isn't caught mid-exploration with no time to compose.
If a single step blows the budget, you're missing per-tool timeouts. The between-step check can't help you if one tool call runs for a minute; each external call needs its own deadline.
If your agent loop timeout fires but the partial answer is confidently wrong, the problem is that the wrap-up isn't honest about its own limits. A time-limited synthesis should explicitly flag what it couldn't verify, not paper over the gaps to sound complete. An answer that says "based on the two sources I checked — I didn't have time to confirm against the third" is trustworthy; one that states a shaky conclusion as fact because it ran out of time is worse than no answer. Make the wrap-up prompt require the caveat, not just permit it.
⚡ Pro tip: Instrument how often each outcome happens — natural finish, step-cap finish, time-cap finish. The ratio is a health signal. A healthy agent finishes naturally the vast majority of the time. A rising share of time-cap finishes is an early warning that something upstream is slowing down — a degraded tool, a shift in query difficulty — often before users start complaining. Your timeout isn't just a safety net; its firing rate is a canary.
⚠️ Common mistake: Treating the timeout as a pure failure and returning nothing. A timeout that discards all the work the agent did is wasteful and user-hostile — the agent may have gathered 90% of the answer. Always attempt a best-effort synthesis on timeout. A partial, honestly-labeled answer beats an error message the user can't act on, and it salvages the tokens you already spent.
Your Turn
Set a budget from your user's real tolerance, make it visible to the model so it converges as time runs low, add per-tool timeouts so no call overruns, and reserve a slice for graceful wrap-up. Test it by artificially shrinking the budget and watching whether the partial answers stay useful under real pressure rather than only in the happy path.
The wrap-up prompt and the urgency-injection wording take iteration to get right — too aggressive and the agent gives up early, too soft and it ignores the clock. Once tuned, keep them. A prompt library like PromptABCD lets you version your timeout and wrap-up blocks so every agent you build handles its clock gracefully from day one, and a graceful clock turns out to be one of the features users notice most — nobody praises a fast agent, but everyone remembers the one that left them staring at a spinner.
Continue Reading
Save the prompts from this post
PromptABCD is a free prompt manager. Paste, organize, and reuse your best AI prompts — no more hunting through chat history.
