Agentic TOCTOU: When an Agent Checks, Then Acts on Changed State
Agentic TOCTOU: an AI agent checks state, reasons for seconds, then acts after it changed. The research behind the term, the shared-state case, and the fixes.
Agentic TOCTOU: an AI agent checks state, reasons for seconds, then acts after it changed. The research behind the term, the shared-state case, and the fixes.
TL;DR: Agentic TOCTOU is a time-of-check to time-of-use bug in an AI agent: it checks a condition, reasons, and acts after the condition changed. Studies in 2025 and 2026 found the flaw in tool-calling and browser-use agents, because state can change between when the agent plans and when it acts. The same thing happens on shared state, where another agent changes a balance or a claim mid-reasoning. Re-checking before acting narrows the window. Checking and acting in one transaction closes it, for state the transaction can cover.
Take an illustrative case. Say an agent is told to buy a part if it’s in stock and under $400. It queries inventory: in stock, $380. It reasons about shipping options for a few seconds, picks one, and places the order. By then the last unit sold to another buyer and the price was updated to $420. The order goes through at the new price, or fails halfway, or reserves stock that isn’t there.
The agent did everything right. It checked before it acted. The problem is the gap in time between the two, and with agents that gap is not an instruction or two. It is a model call.
This failure has a name in security engineering, and it now has a name in agent research too. This post covers where the term comes from, why agents make it worse, and the fixes in order of how much of the window each one closes.
Agentic TOCTOU is a time-of-check to time-of-use flaw in an AI agent: the agent observes state, reasons over it, then acts after that state has changed, so the action rests on a check that is no longer true. The window spans the agent’s reasoning, often seconds long, and can be triggered by ordinary concurrency or by an adversary.
The underlying weakness is catalogued as CWE-367: “The product checks the state of a resource before using that resource, but the resource’s state can change between the check and the use in a way that invalidates the results of the check.” The classic example is a program that checks a file’s permissions, then opens it, while an attacker swaps the file for a link in between.
Two papers are the place to start. The first, Mind the Gap: Time-of-Check to Time-of-Use Vulnerabilities in LLM-Enabled Agents by Derek Lilienthal and Sanghyun Hong, submitted to arXiv in August 2025, describes itself as “the first study of TOCTOU vulnerabilities in LLM-enabled agents.” It looks at tool-calling agents and gets its own section below.
The second, Atomicity for Agents: Exposing, Exploiting, and Mitigating TOCTOU Vulnerabilities in Browser-Use Agents by Linxi Jiang, Zhijie Liu, Haotian Luo and Zhiqiang Lin, submitted to arXiv in February 2026, studies browser agents. They observe a page through the DOM or a screenshot, plan an action, and execute it. In the authors’ words, “web pages often change between planning and execution, causing agents to execute actions based on stale assumptions.” Dynamic or adversarial content can use that window to make the agent click something it never chose.
They tested 10 popular open-source browser agents and report that all 10 fail on at least one manipulation type. Their mitigation is pre-execution validation: while the agent plans, monitors record DOM structure edits, attribute updates and layout shifts, and immediately before the action runs, a validator checks whether anything relevant changed. The full paper reports the trigger ratio dropping to 0% in its tests, with overhead below 0.05 seconds per loop, against the several seconds a single LLM reasoning step typically takes.
The second paper’s title is the useful part for everyone else: atomicity. The fix for TOCTOU has always been to stop the check and the use from being two separate steps.
The 2025 paper looks at agents that call tools in sequence, using tasks from the AgentDojo banking, Slack, travel and workspace environments. Its benchmark, TOCTOU-Bench, has 66 realistic user tasks, and the authors “find that 56 of the 66 tasks contain a possible TOCTOU vulnerability” (full paper). The vulnerable shapes are ordinary pairs of calls. The paper’s examples include reading a web page and then posting what it said to another site, looking up a user’s info and then updating their password, and searching for a file by name and then deleting or sharing it.
They test three defenses: rewriting prompts to discourage plans built on vulnerable call sequences, monitoring tool-call sequences at runtime for known vulnerable pairs, and a tool fuser that replaces a vulnerable pair with “fused tools that execute atomically as a single operation.” In the Slack environment, the fuser cut the average time between the two calls from 1.70 seconds to 0.07 seconds (full paper). All three together reduced the TOCTOU vulnerabilities in executed trajectories “from 12% to 8%” (abstract).
The fuser is the defense to notice. Turning a check and a use into one operation is the same move as the conditional write later in this post. The paper’s own numbers show what fusion leaves: the window shrank to 0.07 seconds, not to zero, and the example fused tool in its first figure still makes the read call and then the post call. Our read is that as long as a tool calls two APIs in sequence, another writer can land between them, so closing the gap takes the system that holds the state doing the check itself.
In ordinary code, the time between a check and a use is a few instructions. For an agent, the check goes into a prompt and the use comes out after the model has reasoned, possibly called other tools, and generated a plan. The Atomicity for Agents paper notes that a single LLM reasoning step typically takes several seconds.
That changes two things. Ordinary concurrency, with no attacker at all, now regularly lands inside the window: another agent, a human user or a batch job writes during those seconds. And the window is predictable, which makes it easier to aim at.
Agents also tend to separate the check and the act into different tools. get_inventory and place_order are two calls. Nothing ties the second to the result of the first unless you build that in.
The classic case is in operating systems, and their advice carries over unchanged. The Linux access(2) man page warns that using it to check permission before calling open(2) “creates a security hole, because the user might exploit the short time interval between checking and opening the file to manipulate it.” The fix it recommends is not a faster check. It is to “temporarily switch the process’s effective user ID to the real ID and then call open(2)” (access(2)), so the operation enforces the permission itself.
CWE-367 puts it more bluntly: “The most basic advice for TOCTOU vulnerabilities is to not perform a check before the use.” Where a separate check can’t be avoided: “Ensure that locking occurs before the check, as opposed to afterwards, such that the resource, as checked, is the same as it is when in use” (CWE-367).
For agents, that maps directly. Don’t rely on get_budget followed by submit_purchase; make the purchase enforce the budget. Where the check has to be separate, take the hold as part of the check, not after it. The fixes below are those two ideas, applied to shared state.
Each fix shrinks the gap between check and act. The last two remove it, for state the store holds.
1. Re-check before acting. Read the state again right before the act and abort if it changed. This is the shape of the browser-agent paper’s pre-execution validation, and on a browser page it’s the right tool, since you can’t wrap a page in a transaction. On shared state it shrinks the window from seconds to the gap between the re-check and the write, which is small but not zero. Two agents can both re-check, both pass and both write.
2. Make the act conditional. Pass what you checked into the act, and have the act succeed only if it still holds. This is compare-and-set.
UPDATE budgets
SET remaining = remaining - 350
WHERE team_id = 42
AND remaining >= 350
RETURNING remaining;
-- zero rows: budget no longer covers it; tell the agent, don't retry blindly
The check and the act are now one statement. Agent B’s write finds remaining = 150 and returns nothing.
3. Check and act in one transaction. When the check spans more than one row, such as “this team’s spend plus open holds stays under budget,” put the reads and the write in one transaction at serializable isolation. In PostgreSQL, Serializable makes concurrent transactions behave as if they ran one at a time; when two conflict, one is rolled back with a serialization error and the application retries it.
BEGIN ISOLATION LEVEL SERIALIZABLE;
SELECT b.limit_amount
- COALESCE(SUM(h.amount), 0) AS available
FROM budgets b
LEFT JOIN holds h ON h.team_id = b.team_id AND h.status = 'open'
WHERE b.team_id = 42
GROUP BY b.limit_amount;
-- application: proceed only if available >= 350
INSERT INTO holds (team_id, amount, agent_id, status)
VALUES (42, 350, 'agent-a', 'open');
COMMIT;
Keep the model call outside the transaction. Let the agent reason on a plain read, then run the short transaction that re-checks and writes. Holding a transaction open across a 10-second model call makes serialization failures far more likely, and any row locks it holds block other writers for the whole call.
Often the act is an API call to someone else’s system: a payment processor, a cloud provider, a vendor. You can’t put that call inside your transaction. Three patterns help.
Reserve first. Write a hold in a store you control, inside the atomic check above, then make the external call, then confirm or release the hold. Your agents can’t oversubscribe what they’ve reserved, even though the external system knows nothing about the reservation.
Use idempotency keys. If the external call is retried, the receiver should recognize the key and not repeat the action. See idempotency.
Plan the compensation. If the external call succeeds and your confirm fails, you need an undo step. That’s the saga pattern.
None of these make the external system’s own state atomic with yours. They make your agents’ side of it consistent.
The fixes above need a store where the check and the act can share a transaction, and where the check sees all the context the decision depends on.
The Tacnode Context Lake™ holds the decision context agents check against and the decisions they write. For state Tacnode holds, such as holds, budgets your agents draw down, per-agent limits, task claims and agent actions, the check and the act run in one ACID transaction with serializable isolation by default. That is the third fix above, applied to the state agents contend on.
For data your own systems hold, such as account records or orders that arrive by change data capture, Tacnode gives the check an internally coherent view: raw rows and derived values like spend this month, computed against the same set of ingested events. It doesn’t make an external ledger atomic with your agent’s act. The reserve-first pattern keeps your agents’ side consistent, with the hold in Tacnode.
Every hold, verdict and action the agent writes becomes context for the next agent’s check, which is what makes the second agent’s check see the first agent’s hold.

Nearly two decades at Microsoft, including the SQL Server engine. Leads engineering for the Tacnode Context Lake™.
Fresh Context Newsletter
The argument behind the posts — what breaks when AI acts on stale data, from the team building the Context Lake. About twice a month.
Book a demo and discover how Tacnode can power your AI-native applications.
Book a Demo