DEV Community

Cover image for AI Agents Don't Fail When They Think. They Fail When Reality Changes.
Atul Kumar
Atul Kumar

Posted on

AI Agents Don't Fail When They Think. They Fail When Reality Changes.

An AI agent can make a perfectly reasonable decision and still do the wrong thing.

That sounds like a model problem at first. Maybe the prompt was unclear. Maybe the model misunderstood the request. Maybe the reasoning wasn't good enough.

But in production, there is another failure mode that is much more interesting.

Sometimes the agent was right when it made the decision.

The problem is that reality changed before the agent acted on it.

A payment that was pending failed. An available product went out of stock. A customer who was eligible for a refund submitted another request. A security policy changed while the agent was processing an operation. Another service, user, or agent modified the state that the first agent was relying on.

The model didn't necessarily misunderstand anything.

It simply acted on yesterday's truth.

And once an AI agent starts interacting with real systems, that distinction becomes extremely important.

An AI Agent Doesn't Reason in a Frozen World

When we build a traditional function, we often imagine something relatively simple:

result = process(input)
Enter fullscreen mode Exit fullscreen mode

The input arrives, the function runs, and the result is returned.

An agent is different.

An agent might first retrieve information from five different services, reason over it, call another tool, receive additional information, revise its plan, and eventually execute an action.

A simplified flow might look like this:

Read → Reason → Decide → Validate → Act
Enter fullscreen mode Exit fullscreen mode

The problem is that the systems underneath this flow don't stop changing while the agent is thinking.

Imagine an agent handling a refund request.

At 10:00:01, it reads:

Order status: Delivered
Payment status: Captured
Refund eligibility: Yes
Fraud status: Clear
Enter fullscreen mode Exit fullscreen mode

The agent reasons that the customer can receive a refund.

At 10:00:03, the payment service receives a chargeback notification.

The state changes:

Payment status: Captured → Chargeback initiated
Enter fullscreen mode Exit fullscreen mode

At 10:00:04, the agent executes the refund.

From the agent's perspective, everything looked fine.

From the system's perspective, the world changed between the decision and the action.

That tiny gap is where a surprising number of production problems can hide.

The Read-Reason-Act Gap

I like to think about this as the read-reason-act gap.

The agent reads a state of the world.

It reasons about that state.

Then it acts on the conclusion.

The dangerous assumption is that the state it is in is still valid when the action happens.

Consider this timeline:

Time ───────────────────────────────────────────────>

Agent
  |
  | Read payment = Pending
  |
  | Read risk = Low
  |
  | Read customer = Verified
  |
  |        Reasoning
  |
  |        Decide: Approve
  |
  |-----------------------------> Approve Payment
  |
  v

Payment Service
        |
        | Payment becomes Flagged
        |
        v
Enter fullscreen mode Exit fullscreen mode

The agent's reasoning may have been completely correct given the information available to it.

The problem is that the information stopped being current.

This is one of the reasons building reliable AI agents has less to do with making the model "smarter" than many teams initially expect.

The model needs good reasoning, but the surrounding system needs to make sure the reasoning is still applicable when execution happens.

A Database Version Check Helps, But It Doesn't Solve Everything

This is where traditional distributed-systems techniques become useful.

Suppose our payment record has a version number:

{
  "payment_id": 123,
  "status": "pending",
  "version": 42
}
Enter fullscreen mode Exit fullscreen mode

The agent reads the version 42.

When it eventually tries to approve the payment, the application performs an optimistic concurrency check:

UPDATE payments
SET status = 'approved',
    version = version + 1
WHERE payment_id = 123
  AND version = 42;
Enter fullscreen mode Exit fullscreen mode

If another process has already changed the payment, the update affects zero rows, and the application knows that its view is stale.

That's good.

But there is an important limitation.

What if the agent's decision didn't depend only on the payment?

Maybe it also looked at:

Payment Service       → Payment status
Risk Service          → Risk score
Customer Service      → Account status
Fraud Service         → Fraud result
Policy Service        → Applicable policy
Inventory Service     → Availability
Enter fullscreen mode Exit fullscreen mode

The payment might still be at version 42 while the fraud service has already changed its assessment.

The payment version check can pass.

The agent's decision can still be wrong.

This is why I don't think of agent consistency as simply a database versioning problem.

It's a decision-input consistency problem.

The Agent's Context Is Really a Snapshot

One of the easiest mistakes to make with agents is to treat retrieved context as truth.

It isn't the truth.

It is a snapshot of what the system looked like when the information was retrieved.

Imagine the agent receives:

Customer: Verified
Risk score: 12
Payment: Pending
Policy: v18
Inventory: Available
Enter fullscreen mode Exit fullscreen mode

The agent doesn't actually know that these things will remain true.

What it knows is:

"These were the values I observed."

That difference may seem small when building a prototype.

In production, it becomes a major architectural concern.

A better mental model is:

Context = Evidence + Version + Timestamp + Freshness requirement
Enter fullscreen mode Exit fullscreen mode

Now the system can reason about whether the information is still usable.

For example:

Payment status
Freshness: Very high

Fraud assessment
Freshness: High

Customer name
Freshness: Low

Policy document
Freshness: Version-specific
Enter fullscreen mode Exit fullscreen mode

Not every piece of context needs to be refreshed constantly.

The important thing is knowing which pieces matter for the action you're about to take.

The Decision Should Carry Its Dependencies

One practical approach is to record the inputs that influenced a consequential decision.

Suppose an agent decides to approve a payment.

Instead of storing only:

{
  "action": "approve_payment",
  "payment_id": 123
}
Enter fullscreen mode Exit fullscreen mode

The system could retain something closer to:

{
  "action": "approve_payment",
  "payment_id": 123,
  "decision_inputs": {
    "payment_version": 42,
    "risk_version": 19,
    "customer_version": 63,
    "policy_version": 7
  }
}
Enter fullscreen mode Exit fullscreen mode

Now the execution layer knows what the agent actually relied on.

Before executing, it can decide which dependencies need to be checked again.

For example:

def validate_action(decision):
    if payment.version != decision["payment_version"]:
        return False

    if risk.version != decision["risk_version"]:
        return False

    return True
Enter fullscreen mode Exit fullscreen mode

This doesn't mean every value must be perfectly synchronised.

It means the system has an explicit way of saying:

"This action was based on these inputs, and these are the inputs that matter enough to validate."

That is a much stronger architecture than simply trusting whatever happens to be sitting in the agent's context window.

ETags Are Another Piece of the Same Idea

This doesn't have to be implemented only with database version columns.

HTTP already gives us a useful mechanism through ETags.

A service might return:

ETag: "payment-v42"
Enter fullscreen mode Exit fullscreen mode

The agent or execution layer can retain that value.

When performing an update, the client can send:

If-Match: "payment-v42"
Enter fullscreen mode Exit fullscreen mode

The server can then reject the operation if the resource has changed since the agent read it.

Conceptually:

The agent reads the resource
        |
        | ETag = v42
        |
        v
Agent reasons
        |
        | Resource changes to v43
        |
        v
Agent attempts update
        |
        | If-Match: v42
        |
        v
Server rejects stale update
Enter fullscreen mode Exit fullscreen mode

This doesn't solve every agent-consistency problem, but it gives the execution layer a useful precondition: only act if the state is still the state I reasoned about.

The More Dangerous Case Is When Everything Looks Fine

The most difficult production bugs aren't always the ones where something obviously fails.

Imagine an agent approves an order.

The database update succeeds.

The API returns 200.

No exception is thrown.

The workflow is marked as successful.

Later, someone discovers that the order should never have been approved because the customer's fraud status changed while the agent was processing it.

From an infrastructure perspective:

Database       ✓
API            ✓
Authorization  ✓
Tool call      ✓
Transaction    ✓
Enter fullscreen mode Exit fullscreen mode

From a business perspective:

Outcome        ✗
Enter fullscreen mode Exit fullscreen mode

This is why monitoring only technical success metrics isn't enough for agentic systems.

We need to know not only whether an action succeeded, but whether the decision remained valid throughout execution.

This Changes How We Think About Observability

Traditional application logs might tell us:

10:00:01 Payment read
10:00:04 Payment approved
10:00:04 API returned 200
Enter fullscreen mode Exit fullscreen mode

That's useful, but incomplete.

For an agent, I would want to know something closer to:

Decision ID: 8f32

Payment version observed: 42
Risk version observed: 19
Policy version observed: 7

Decision created: 10:00:02
Execution started: 10:00:04

Payment version at execution: 43
Risk version at execution: 19
Policy version at execution: 7

Action: Rejected
Reason: Payment state changed
Enter fullscreen mode Exit fullscreen mode

Now we have an explanation.

The agent didn't necessarily "hallucinate."

The decision became stale.

That distinction matters for debugging, incident reviews, and improving the system.

Another Agent Can Change the World Too

External systems aren't the only source of change.

Another agent can create the same problem.

Imagine two agents working on customer accounts.

Agent A reads:

Credit available: $50,000
Enter fullscreen mode Exit fullscreen mode

Agent B reads the same value.

Agent A approves a $40,000 transaction.

Agent B approves another $40,000 transaction for a different account.

Both agents might successfully update their own records.

There may be no conflicting write on either individual account.

But the shared credit limit has now been exceeded.

This is a classic write-skew scenario.

The important lesson is that protecting individual records isn't always enough.

Sometimes the system needs to protect the business invariant itself.

For example:

Total credit exposure <= $100,000
Enter fullscreen mode Exit fullscreen mode

That constraint exists across multiple records.

A version check on Account A doesn't protect it.

A version check on Account B doesn't protect it.

The shared constraint needs to participate in the consistency mechanism.

This is where agent systems start inheriting some of the hardest problems from distributed systems.

Don't Confuse Stale Decisions With Duplicate Actions

There is another distinction worth making here.

Suppose an agent sends a payment request, times out, and doesn't know whether the payment was processed.

It retries.

That's an idempotency problem.

A request might use:

Idempotency-Key: payment-123-abc
Enter fullscreen mode Exit fullscreen mode

So the server can recognise repeated attempts to perform the same logical operation.

That's important, but it solves a different problem.

Idempotency protects against duplicate execution.

It doesn't tell us whether the original decision was still valid.

A useful way to think about these mechanisms is:

Optimistic locking
→ Did the record change?

ETag / If-Match
→ Is the resource still the version I observed?

Read-set validation
→ Are the important inputs behind my decision still valid?

Idempotency
→ Am I accidentally executing the same action twice?

Invariant validation
→ Does this action still preserve the business constraint?
Enter fullscreen mode Exit fullscreen mode

A production agent can need all of them.

The Model Shouldn't Be the Final Consistency Layer

This is probably the most important architectural lesson.

A language model is very good at interpreting information, planning, and choosing between possible actions.

It should not be expected to guarantee that the world hasn't changed since it made its decision.

That responsibility belongs in the surrounding system.

A safer architecture looks something like this:

                  ┌───────────────┐
                  │   AI Agent    │
                  │               │
                  │ Read + Reason │
                  └───────┬───────┘
                          │
                     Proposed Action
                          │
                          ▼
                 ┌─────────────────┐
                 │ Decision Layer  │
                 │                 │
                 │ Inputs          │
                 │ Versions        │
                 │ Preconditions   │
                 └────────┬────────┘
                          │
                          ▼
                 ┌─────────────────┐
                 │   Validator     │
                 │                 │
                 │ Freshness       │
                 │ Concurrency     │
                 │ Invariants      │
                 └────────┬────────┘
                          │
                  ┌───────┴───────┐
                  │               │
                Invalid          Valid
                  │               │
                  ▼               ▼
              Re-read          Execute
              Re-plan             │
                                  ▼
                          External System
Enter fullscreen mode Exit fullscreen mode

The agent proposes.

The deterministic execution layer verifies.

Only then does the action happen.

That separation becomes increasingly important as agents gain more authority.

What Happens When the State Changes?

A stale decision doesn't always mean the workflow should simply fail.

There are several possible responses.

If the change is minor, the system may refresh the affected input and continue.

If the change affects the core business decision, the agent may need to reconsider.

If the action is high-risk, the system may require human approval.

For example:

State unchanged
    ↓
Execute

State changed, low impact
    ↓
Refresh input
    ↓
Continue

State changed, decision-critical
    ↓
Re-plan
    ↓
Validate again

High-risk conflict
    ↓
Human approval
Enter fullscreen mode Exit fullscreen mode

This gives the system a more useful response than simply saying:

VERSION_CONFLICT
Enter fullscreen mode Exit fullscreen mode

The goal isn't just to detect that reality changed.

The goal is to respond appropriately when it does.

Building Agents for a World That Changes

I think one of the biggest differences between an AI demo and a production agent is how they treat time.

A demo often looks like this:

Question → Model → Tool → Answer
Enter fullscreen mode Exit fullscreen mode

Production looks more like:

Read state
   ↓
Reason
   ↓
State changes
   ↓
Validate assumptions
   ↓
Re-read critical inputs
   ↓
Check permissions
   ↓
Check business invariants
   ↓
Execute
   ↓
Verify outcome
Enter fullscreen mode Exit fullscreen mode

That additional complexity isn't accidental.

It is the price of allowing software to take actions in a world that continues moving while the software is thinking.

And this is why I don't think the next generation of reliable AI agents will be built only by improving prompts or increasing model intelligence.

They will be built by combining good reasoning with good systems engineering.

Final Thought

AI agents don't operate in a static database.

They operate in a world where users click buttons, services update records, payments settle, policies change, other agents take actions, and failures happen independently.

The agent can make the right decision at 10:00:01 and the wrong decision at 10:00:04 simply because the world changed in between.

That doesn't make the model useless.

It means we need to design the system around the model differently.

The agent should know what information it used.

The system should know which of those inputs matter.

Critical assumptions should be revalidated before consequential actions.

Shared business constraints should be protected.

And when reality changes, the system should be able to re-read, re-plan, or stop instead of blindly executing an old decision.

The question isn't only:

"Can the agent make the right decision?"

The more important production question is:

"Can the system make sure that the decision is still valid when the agent acts?"

That's the difference between an AI agent that works in a demo and one that can be trusted with real work.

Top comments (0)