Subtitle: Why long-running AI agents make incorrect decisions even when their reasoning is sound, and how to design systems that detect outdated information before acting.
An AI agent can make the right decision at the wrong moment. Imagine an agent handling a customer's order. It retrieves the order details, checks the payment status, reviews the refund policy, and decides that the customer is eligible for a refund.
Everything looks correct. But before the agent executes the refund, another service updates the order. Perhaps the customer has already received a refund through a different channel. Perhaps the order has been cancelled, or the payment status has changed.
The agent does not know about this update. It continues working with the information it retrieved earlier and attempts to execute an action based on an outdated version of reality.
The reasoning may be perfectly valid. The context is not. This is one of the problems I find particularly important when thinking about production AI systems. We spend considerable time improving how agents reason, use tools, retrieve information, and maintain memory. But there is another question that deserves just as much attention:
How does an AI agent know that the information it remembers is still valid?
Memory helps an agent maintain continuity. It does not guarantee that the information in that memory is current.
That distinction becomes increasingly important as agents operate for longer periods, interact with multiple services, and coordinate work across different systems.
1. The problem with treating memory as truth
Most AI agents work with some form of context.
This context can include:
- Previous conversation messages.
- Information retrieved from databases.
- API responses.
- Cached records.
- Outputs from other agents.
- Tool execution results.
- Previous decisions and workflow states.
All of these sources help an agent understand what is happening.
However, they do not all have the same reliability or lifetime.
A conversation message might remain useful for weeks. An account balance might become outdated within seconds. A customer's delivery address may change while an order is being processed. A permission that was valid earlier may have been revoked.
The problem begins when an agent treats all this information as equally reliable. Consider a simple example:
At 10:00 AM, an agent retrieves an order:
{
"order_id": "ORD-1042",
"status": "paid",
"refund_status": "not_processed",
"version": 12
}
The agent evaluates the information and determines that a refund can be processed.
At 10:03 AM, another service processes the refund and updates the order:
{
"order_id": "ORD-1042",
"status": "paid",
"refund_status": "processed",
"version": 13
}
At 10:04 AM, the original agent attempts to continue using the information it retrieved at 10:00 AM.
Its internal context still says:
refund_status = not_processed
The agent is not necessarily making a reasoning mistake. It is making a decision using a record that was once accurate but has since become outdated.
This is the difference between remembering a fact and verifying a fact.
An agent's memory should help it understand the history of a task. It should not automatically become the authority for decisions that depend on the current system state.
2. Why long-running agents make this problem worse
A short interaction may retrieve information and act almost immediately. The time between retrieval and execution is relatively small.
Long-running agents are different.
They may spend several minutes or hours planning, retrieving information, waiting for human approval, calling external services, or coordinating with other agents.
During that time, the underlying environment can change.
I see several situations where this creates particular risks.
Long-running workflows
An agent may retrieve customer information at the beginning of a workflow and use it much later. If the customer's account status changes in between, the original information may no longer be suitable for the next decision.
Multi-agent communication
One agent may retrieve information and pass it to another agent. The receiving agent may assume that the information is current without knowing when it was originally collected.
External API dependencies
An API response is a snapshot of a system at a particular point in time. It does not guarantee that the returned values will remain unchanged.
Human approval delays
An action may be reviewed and approved, but the underlying business state can change before execution. An approval based on an earlier snapshot does not make that snapshot permanently valid.
Cached information
Caching reduces repeated requests and can improve performance. However, cached data needs an explicit freshness strategy, especially when it is used to make decisions involving payments, access permissions, inventory, or customer records.
These situations have something in common: the agent's context and the actual state of the system can gradually become different.
The longer the gap between retrieval and execution, the more important it becomes to verify the information again.
3. Memory, context and source of truth are different things
I think it is useful to separate these three concepts when designing agent systems.
Memory preserves information from previous interactions and tasks.
Context provides the information an agent uses to make its current decision.
Source of truth is the authoritative system responsible for maintaining a particular piece of information.
For example, an agent may remember that a customer requested a refund. Its current context may contain the original order details and the result of an earlier eligibility check.
But the order management or payment system should remain authoritative for the current refund status.
The distinction matters because an agent can have a perfectly consistent memory that is no longer accurate.
A useful design principle is:
Memory should preserve what happened. The source of truth should establish what is true now.
This does not mean agents should retrieve everything again before every operation. That would create unnecessary latency and additional service load.
Instead, the system should identify which information can safely be reused and which information must be validated before a decision or action.
For example, a user's preferred communication style may not need frequent validation. A payment status or access permission usually deserves much stricter treatment.
Freshness requirements should depend on the consequences of acting on outdated information.
4. Context freshness should be part of the architecture
One practical approach is to attach metadata to important pieces of context.
Instead of storing only a value, store information about where it came from and when it was retrieved.
For example:
from dataclasses import dataclass
from datetime import datetime, timezone
from typing import Any
@dataclass
class ContextRecord:
value: Any
source: str
retrieved_at: datetime
version: int
max_age_seconds: int
def is_fresh(self) -> bool:
now = datetime.now(timezone.utc)
age = (now - self.retrieved_at).total_seconds()
return age <= self.max_age_seconds
Now, the agent's context contains more than just the order status.
It also knows:
- Which system supplied the information?
- When the information was retrieved.
- Which version of the record was returned?
- How long can the information be considered reusable?
For example:
order_context = ContextRecord(
value={
"order_id": "ORD-1042",
"status": "paid",
"refund_status": "not_processed"
},
source="order_management_api",
retrieved_at=datetime.now(timezone.utc),
version=12,
max_age_seconds=60
)
The freshness check is straightforward:
if not order_context.is_fresh():
print("Context is outdated. Refresh required.")
else:
print("Context is within its freshness window.")
This is a useful starting point, but there is an important limitation.
A time-based freshness check does not prove that information is still correct.
An order can change one second after retrieval. A record that is only five seconds old can already be outdated.
A freshness window reduces the period during which stale information may be reused. It does not eliminate the risk.
For sensitive operations, time-based checks should be combined with authoritative state validation.
5. Revalidate important information before execution
This is where the design becomes more interesting.
Rather than allowing an agent to execute an action using an old snapshot, the execution layer should check the current state.
The following example illustrates a simplified approach:
class StaleContextError(Exception):
pass
def validate_order_before_refund(
stored_order,
current_order
):
if current_order is None:
raise StaleContextError(
"Order could not be found."
)
if stored_order["version"] != current_order["version"]:
raise StaleContextError(
"Order has changed since retrieval."
)
if current_order["refund_status"] == "processed":
raise StaleContextError(
"Refund has already been processed."
)
if current_order["status"] != "paid":
raise StaleContextError(
"Order is not eligible for a refund."
)
return True
The execution flow would look something like this:
def process_refund(
stored_order,
order_service,
payment_service
):
current_order = order_service.get_order(
stored_order["order_id"]
)
validate_order_before_refund(
stored_order,
current_order
)
return payment_service.create_refund(
order_id=current_order["order_id"]
)
The important part is not the complexity of the code. It is the separation of responsibilities.
The agent can propose an action based on its context. The execution layer retrieves the latest authoritative record and checks whether the action is still valid.
If the record has changed, execution stops.
The system can then ask the agent to refresh its context, reconsider the decision, or request another approval if necessary.
There is another important engineering detail here: this example performs a read followed by a separate action. In a concurrent system, the order could change after validation but before the refund is created.
That is a time-of-check-to-time-of-use problem.
For high-impact operations, validation and execution should be protected by an appropriate atomic operation, database transaction, conditional update, or provider-supported version check.
A separate read immediately before an action is better than trusting an old snapshot, but it is not always sufficient.
6. Versioning is more useful than timestamps alone
Timestamps help identify how old the information is.
Versions help identify whether the underlying record has changed.
Imagine an agent retrieves an order at version 12. Another service updates the order to version 13.
Even if only a few seconds have passed, the agent can detect that it is no longer working with the latest version.
This is particularly useful when multiple services update the same business record.
A simplified conditional update might look like this:
UPDATE orders
SET refund_status = 'processing',
version = version + 1
WHERE order_id = 'ORD-1042'
AND version = 12
AND refund_status = 'not_processed';
If no row is updated, the expected version or state no longer matches.
The application should then stop and retrieve the latest record.
This is an example of optimistic concurrency control. Instead of locking every record while an agent thinks, the system allows work to proceed but checks whether the original assumptions still hold when a change is attempted.
For distributed systems, version numbers, entity revisions, ETags, and conditional writes can all help establish whether the data being used is still the expected version.
However, versioning needs to be designed carefully.
The version must be generated and maintained by the authoritative system. An agent should not be allowed to invent or update its own version number to make an outdated record appear current.
7. The bigger challenge: stale context in multi-agent systems
Stale context becomes more difficult when agents communicate with each other.
Consider a workflow involving three agents:
- A research agent retrieves customer and order information.
- A decision agent evaluates the information.
- An execution agent performs the requested operation.
The research agent retrieves an order at version 12 and sends the information to the decision agent.
The decision agent approves a proposed action based on that information.
Before the execution agent receives the task, another service updates the order to version 13.
The execution agent now has a decision that was valid against an earlier snapshot.
This problem cannot be solved simply by giving every agent more memory.
In fact, more memory can make the problem harder if the system does not distinguish between historical information and the current state.
A better approach is to pass context with its provenance.
For example:
{
"task_id": "TASK-204",
"entity": "order",
"entity_id": "ORD-1042",
"source": "order_management_api",
"source_version": 12,
"retrieved_at": "2026-11-07T09:30:00Z",
"decision": "refund_eligible",
"status": "pending_execution"
}
The receiving agent can use this information to understand the decision and its origin.
But the execution service must still verify the current version before operating.
This distinction is important:
Passing context between agents should transfer information, not automatically transfer trust.
Each service should understand the authority and freshness of the information it receives.
An agent-to-agent message is not a substitute for checking the system that owns the underlying data.
8. Event-driven invalidation can help
Another useful design approach is event-driven context invalidation.
Instead of relying entirely on periodic freshness checks, systems can publish events when important records change.
For example:
{
"event": "order.updated",
"order_id": "ORD-1042",
"previous_version": 12,
"new_version": 13,
"changed_fields": [
"refund_status"
]
}
An agent orchestration layer can consume this event and identify any active workflows that depend on the affected order.
It can then:
- Mark the relevant context as potentially stale.
- Invalidate affected cached records.
- Pause workflows that depend on the previous version.
- Trigger a fresh retrieval before execution.
- Request a new decision if the change affects the original plan.
This is especially useful in systems where multiple agents work on overlapping records.
However, event-driven invalidation has its own limitations.
Events can be delayed, duplicated, or delivered out of order. A consumer may temporarily lose their connection. A workflow may attempt execution before receiving an update event.
For that reason, event notifications should be treated as an additional freshness mechanism, not the final authority.
A useful combination is:
- Event-driven invalidation to identify changes quickly.
- Version checks to detect changes to individual records.
- Authoritative validation at the execution boundary.
- Recovery logic when the original context is no longer valid.
The system should remain safe even if an event is missed.
9. What should an agent do when its context becomes stale?
Detecting stale context is only half the problem.
The system also needs a defined response.
An agent should not automatically continue using outdated information just because it has already completed most of the workflow.
I would separate the response into four steps.
Step 1: Stop the affected action
If the system detects a version mismatch or invalid state, it should prevent the action from proceeding.
Step 2: Retrieve the latest information
The agent or orchestration layer should fetch the relevant information from the authoritative source.
Step 3: Re-evaluate the decision
The system should determine whether the original decision remains valid.
A change in an unrelated field may not affect the action. A change in refund status, account ownership, payment amount, or permissions may require a completely new decision.
Step 4: Obtain fresh authorisation when necessary
If the action's important parameters have changed, the original approval should not automatically carry over.
This is particularly important for operations involving money, access rights, customer data, and irreversible external actions.
There is a useful connection here with approval security.
In my earlier writing about AI agent approvals, I explored why authorising a general intention is not the same as authorising a specific action. Stale context creates a related problem: even an action that was valid when approved may no longer be valid when executed.
The system must verify both what was authorised and whether the underlying conditions still support execution.
10. Testing stale context should be part of agent evaluation
Many agent evaluations focus on whether the model produces the expected answer.
That is necessary, but it is not enough for systems that interact with a changing external state.
I would also test what happens when information changes between retrieval and execution.
Here are some practical scenarios worth including.
| Test scenario | Expected system behaviour |
|---|---|
| Order changes after retrieval | Detect version mismatch and stop execution |
| Refund processed by another service | Reject duplicate refund attempt |
| User permission revoked | Revalidate permissions and deny action |
| Agent receives an old message | Verify the referenced source version |
| Update event arrives late | Execution-time validation still catches stale state |
| Two agents modify the same record | Prevent conflicting updates through concurrency controls |
| Agent retries after a timeout | Use idempotency and reconcile the original operation |
| Unrelated field changes | Apply field-level or domain-specific validation where appropriate |
A particularly important test is the gap between validation and execution.
For example:
- Retrieve an order at version 12.
- Validate that a refund is permitted.
- Update the order to version 13 through another service.
- Attempt the original refund operation.
- Confirm that the execution boundary rejects the stale version or that the atomic conditional operation prevents the conflict.
The test should verify not only the agent's response but also whether the external operation was actually blocked.
A message saying "the order has changed" is not a security control if the payment request has already been sent.
11. Freshness has a cost, so it needs to be designed intelligently
There is no reason to refresh every piece of context continuously.
Doing so would increase API calls, processing costs, latency, and pressure on external services.
The better approach is to classify information according to its importance and how quickly it can change.
For example:
| Information type | Suggested strategy |
|---|---|
| User communication preferences | Longer-lived memory |
| Product descriptions | Cached with periodic refresh |
| Inventory availability | Short freshness window and version validation |
| Payment status | Authoritative validation before action |
| Account permissions | Recheck at execution |
| Refund eligibility | Validate current order and payment state |
| Historical conversation | Preserve as historical context |
These are illustrative categories, not universal expiry settings. Actual freshness windows should depend on the business process, the system's update frequency, and the consequences of using outdated data.
A useful way to think about it is that freshness should be proportional to risk.
If outdated information only affects a recommendation, a slightly older snapshot may be acceptable.
If outdated information can trigger a financial transaction or change access permissions, the execution requirements should be much stricter.
The objective is not to eliminate memory or caching. It is to prevent convenience mechanisms from becoming accidental sources of authority.
12. What this means for production AI architecture
As AI agents become more capable, the architecture around them becomes just as important as the model itself.
An agent can plan a workflow, retrieve information, communicate with other agents, and decide which tool to call. But the system still needs to establish whether the information supporting that decision is current.
This is particularly relevant to predictive and orchestration-based systems.
In an architecture involving multiple data sources, models, and workflow stages, information can pass through several components before it influences an action. Each component may operate on a different snapshot of the underlying environment.
If the architecture does not preserve provenance, version information, and validation requirements, it becomes difficult to understand whether a decision was based on current data or an outdated result.
For me, this is an important part of building reliable AI orchestration: the system should track not only what a model decided, but also which information supported that decision and whether the information remained valid when the decision was executed.
A reliable architecture should therefore consider:
- Provenance: Where did the information originate?
- Versioning: Which revision of the underlying record was used?
- Freshness: When was the information retrieved?
- Invalidation: What happens when the underlying record changes?
- Revalidation: Which checks must happen before execution?
- Recovery: How does the workflow respond when its assumptions are no longer valid?
These are not problems that can be solved by a better prompt alone.
They require coordination between the model, orchestration layer, data services, and execution infrastructure.
Conclusion: An agent should remember the past, but verify the present
I believe one of the most important lessons in production AI is that better reasoning does not automatically produce more reliable execution.
An agent can make a sensible decision, explain its reasoning, and follow the instructions it received. Yet the action can still be wrong if the information behind that decision has changed.
This is why memory and source-of-truth systems need clearly defined responsibilities.
Memory provides continuity. Context supports reasoning. Authoritative systems establish the current state. Execution-time validation ensures that an action is still allowed when it actually happens.
As agents become longer-running and more interconnected, stale context will become an increasingly important engineering concern.
The question should not simply be whether an AI agent remembers the right information.
It should be whether the system can prove that the information is still relevant before allowing the agent to act.
An AI agent does not need to forget everything it knows. It needs to know when what it remembers is no longer enough.
Top comments (7)
This is the failure I hit, and it was dull rather than clever: I told someone the version and checksum of a file I had shipped, straight from memory. My memory had quietly merged two separate shipments into one story. The file was fine. My report about it was fiction.
What fixed it was bureaucratic. A plain ledger file I have to open before I say anything about a delivered artifact, one row per send with recipient, bytes, checksum, and the result of reading it back from the public URL. The row is the authority now, and the memory of the work is optional.
The other half of your argument I'd underline: a tool saying "written" is a record of what was attempted. Reading the bytes back from where they actually landed is the only thing that counts as happened.
This is a great example of how stale or merged context can create problems even when the underlying work was completed correctly. What stood out to me is that the file itself was fine, but the memory of which version was actually shipped had become unreliable. I really like your approach of maintaining a separate ledger with the recipient, file size, checksum, and read-back result. It creates an auditable record that can be checked independently instead of relying on what an agent or person remembers.
Your second point is equally important. A tool reporting "written" only confirms that an operation was attempted or acknowledged. It does not necessarily prove that the expected artefact is accessible, complete, or correct at its destination.
I'd extend that principle to AI agents generally: execution records should distinguish between an action being requested, an operation being acknowledged, and its outcome being independently verified. Memory can help explain the history, but the evidence of the actual result should remain authoritative. Thanks for sharing this. It adds a very practical dimension to the discussion.
memory is just what the agent thought last time not whats true now. same problem when you plan trips - the plan you saved and the live booking state diverge fast. we built searchspot around rechecking fit before you commit for that reason
Absolutely. I think travel planning is a great example because a saved itinerary can look perfectly valid while the actual booking situation has already changed. Availability, prices, schedules and booking conditions can all move independently of what was originally planned. Your point about rechecking fit before committing is important. It is not enough to remember that an option was suitable when it was first selected. The system needs to verify that it is still suitable at the moment the user makes a decision.
That's also closely related to what I discuss in the article: memory should provide continuity, not become the source of truth. Whether we're dealing with AI workflows, travel plans or bookings, the live state needs to take priority over an earlier snapshot.
Interesting approach with Searchspot. I like the idea of making that revalidation part of the workflow rather than treating it as an optional final check.
The ORD-1042 example makes the distinction clean: version 12 at 10:00, version 13 at 10:03, and a refund decision at 10:04 that is logically sound but factually stale. Your point that a freshness window only narrows the risk, rather than eliminating it, is the part I would stress — a five-second-old record can already be wrong, so time alone is never proof. The conditional UPDATE on version=12 is the stronger check, but it still leaves the TOCTOU gap you flag between the read and the write. How do you handle a version bump caused by an unrelated field change: does the execution layer re-evaluate the whole decision, or only re-check the fields the refund actually depends on?
That's an important distinction. My preference would be to re-evaluate the fields and conditions that the decision actually depends on, rather than automatically rejecting every action because any field on the record has changed. For example, if a refund decision depends on the order ID, payment status, refund status, refundable balance, amount and currency, a change to an unrelated field, such as an internal description, should not necessarily invalidate the decision.
However, I wouldn't rely on a simple field-by-field comparison alone. Dependencies can be indirect. A change in customer status or a business policy might affect refund eligibility even if the original refund fields remain unchanged. I'd approach this by maintaining an explicit set of decision dependencies and validating them against the authoritative state at execution time. Where possible, the final update should use an atomic conditional operation or transaction so that the relevant state cannot change between validation and execution.
If a relevant dependency has changed, the system should stop and re-evaluate the decision. If only an unrelated field has changed, execution can proceed provided the required conditions still hold. So the principle is not necessarily that every version change invalidates the decision. Every assumption supporting the decision must still be valid when the action is executed.
The execution check protects the refund, and the conditional UPDATE protects it against the race. One thing neither protects is the memory itself. The decision record from section 7, refund_eligible against version 12, is still in the store after the execution layer rejects it, and a week later it can come back from retrieval as a fact about this customer.
The StaleContextError is the cheapest moment to fix that. It already knows the entity, the version the decision was made against and the version that is current, so it can write a correction that names the stale record, instead of leaving two disagreeing records for retrieval to sort out. Without that write-back the gate keeps blocking actions while the memory keeps proposing them.