Back to Theory
Memory7 min read · July 13, 2026

Memory Staleness Is the Open Problem Decay Doesn't Solve

Decay fades low-relevance memories over time, but staleness — a frequently-recalled fact that's now factually wrong — resists decay entirely. It needs explicit invalidation, not a better decay curve.

F
Feather DB
Engineering

An AI agent's memory system can be tuned, weighted, and decayed to perfection and still confidently tell a user something that stopped being true six months ago. This is memory staleness, and it is a different failure mode from memory decay — one that decay scoring cannot fix, and in some cases makes worse. Recent research on open-source memory frameworks draws this distinction explicitly: decay handles irrelevance, but a highly-retrieved, high-importance memory becoming factually wrong because the world changed is a harder, largely unsolved problem. This post is a technical walkthrough of why that's true, including where Feather DB's own decay model falls short of solving it.

Decay and staleness are answering different questions

Memory decay answers: how relevant is this memory right now, given how long it's been since it mattered? It's a function of time and retrieval frequency. Feather DB's adaptive decay is a concrete example of this logic:

stickiness = 1 + log(1 + recall_count)
effective_age = age_in_days / stickiness
recency = 0.5 ** (effective_age / half_life_days)
final_score = ((1 - time_weight) * similarity + time_weight * recency) * importance

With a default half_life_days=30 and time_weight=0.3, a memory that keeps getting recalled ages slower than the calendar. A node recalled 20 times has stickiness ≈4.09 — it effectively ages at about 24% of the normal rate, behaving roughly 4x "younger" than its actual age. That's correct behavior for relevance. A fact you keep referencing should keep surfacing.

Memory staleness answers a completely different question: is this memory still true? That's not a function of time or retrieval count at all — it's a function of whether the real-world state the memory describes has changed. A memory can be maximally relevant (retrieved constantly, high importance, low effective age) and simultaneously, completely wrong. Decay scoring has no mechanism to detect that, because it was never designed to evaluate truth. It was designed to evaluate attention.

Why high stickiness makes staleness worse, not better

This is the counterintuitive part. Under a decay model, the memories most resistant to fading are the ones recalled most often. In most cases that's desirable — frequently-used context should persist. But if a frequently-recalled fact goes stale, the same mechanism that keeps useful memories alive now keeps a wrong memory alive, and keeps it ranked near the top of retrieval results.

Take the canonical example: a CRM agent stores that a contact works at Acme Corp. Every call, every email thread, every summary references "Acme Corp" as the contact's employer. Recall count climbs. Stickiness climbs with it. Then the contact changes jobs. Nothing in the agent's memory pipeline observes that change unless something explicitly tells it to. The old employer fact isn't just present in memory — it's the highest-confidence, most decay-resistant memory the agent has about that contact, precisely because it was referenced so often before it became wrong. Decay optimizes for a variable — attention over time — that is orthogonal to correctness. A high-stickiness memory and a stale memory look identical to a scoring function that only measures similarity, recency, and importance.

No current memory architecture infers contradiction automatically

It's worth being specific about what's actually missing, because "staleness detection" sounds like it should be a solvable retrieval problem. It isn't, for a structural reason: vector similarity search finds semantically related memories, not logically contradictory ones. "Works at Acme Corp" and "works at Globex Inc" are similar-shaped statements about the same entity, but nothing about their embeddings signals that they can't both be current. Cosine similarity has no concept of temporal exclusivity.

This is true of Feather DB as shipped, and it's true of essentially every embedded or hosted vector memory system in 2026 that relies on similarity + recency + importance scoring. None of them ship a contradiction detector that fires on ingest. What they can do is store both facts, rank the newer one higher if recency is weighted appropriately, and let the old one keep surfacing for anything that references its retrieval history heavily. Without an explicit signal, the system has no way to know one fact supersedes the other rather than simply coexisting with it, the way "likes coffee" and "likes tea" coexist without contradiction.

Solving this generally would require the memory layer to reason about factual relationships between stored items — essentially a lightweight entailment or contradiction check — which is a different kind of computation than nearest-neighbor search. That's not a gap specific to any one product; it's a gap in the current approach to agent memory as an industry.

Practical mitigations available today

None of these are automatic. All of them require the application layer to supply the signal that the memory store cannot infer on its own.

  • Explicit overwrite on contradiction. When new information arrives that conflicts with a stored fact, call an update/delete on the old record rather than just inserting the new one alongside it. This requires the calling application to already know it's looking at a contradiction — often via a structured field (job title, status, plan tier) rather than free text.
  • Namespace-scoped "current" vs. "historical" facts. Separate mutable state (current employer, current plan, current preference) from append-only event history (past interactions, past purchases). Only the "current" namespace needs contradiction handling; the historical namespace is supposed to accumulate, and old entries there aren't stale — they're just old, which decay handles fine.
  • Periodic re-verification jobs. For facts sourced from an external system of record (CRM, billing, directory service), run a scheduled reconciliation pass that re-checks high-importance, high-recall-count memories against the source of truth and updates or invalidates them if they've drifted.
  • LLM-driven contradiction check on ingest. Before writing a new fact, retrieve the top-k most similar existing memories and ask a model whether the new item contradicts any of them. This adds latency and cost to every write, so it's typically reserved for high-value, low-volume fact types rather than every message in a conversation.

A conceptual illustration of the overwrite pattern — this is not a documented API, just the shape of the logic:

import feather_db

db = feather_db.connect("crm_memory.feather")

def update_fact(entity_id, field, new_value, source):
    existing = db.query(
        filter={"entity_id": entity_id, "field": field},
        top_k=1,
    )
    if existing and existing[0]["value"] != new_value:
        db.update(
            id=existing[0]["id"],
            content=f"{field}: {new_value}",
            metadata={
                "entity_id": entity_id,
                "field": field,
                "value": new_value,
                "superseded_from": existing[0]["value"],
                "source": source,
                "verified_at": "2026-07-17",
            },
        )
    else:
        db.add(
            content=f"{field}: {new_value}",
            metadata={"entity_id": entity_id, "field": field, "value": new_value, "source": source},
        )

update_fact("contact_412", "employer", "Globex Inc", source="linkedin_sync")

The load-bearing part isn't the API shape — it's that update_fact knows, from a structured field comparison, that this is a contradiction rather than an unrelated new memory. Nothing in the vector store discovered that on its own.

What this means for how you design agent memory

Decay tuning — half-life, time weight, importance scoring — is worth getting right, but it's solving relevance, not correctness. If your agent stores facts that change over time (employment, subscription status, contact info, project ownership, pricing), treat those as mutable state with an explicit update path, not as append-only memories you trust decay to eventually deprioritize. Decay might take months to demote a stale fact that's still getting recalled; staleness needs to be fixed the moment the underlying fact changes, not whenever its recall-weighted age eventually catches up.

The practical dividing line: if a fact can be superseded, it needs an identity (an entity + field key) so a write can check for and replace a prior value. If a fact is just an event that happened, decay is the right tool and no invalidation logic is needed.

FAQ

Is memory staleness just a decay tuning problem — would a shorter half-life fix it?

No. A shorter half-life makes all memories fade faster regardless of whether they're still true, which degrades genuinely persistent facts along with stale ones. Staleness is about correctness, not age, so no time-based parameter can target it specifically.

Does high recall count make a memory more likely to go stale?

Recall count doesn't cause staleness — the real world changing does. But high recall count does make a stale memory more dangerous, because it's exactly what keeps that memory decay-resistant and near the top of retrieval results after it's stopped being true.

Can an LLM just catch stale facts during generation instead of fixing memory?

Sometimes, if the contradiction is in the same context window and the model happens to notice it. That's not reliable — it depends on both facts being retrieved together and the model treating the newer one as authoritative, which isn't guaranteed. It's a fallback, not a substitute for invalidation at write time.

Does Feather DB have a built-in staleness detector?

No. Feather DB's decay model handles relevance (stickiness, recency, importance) but does not automatically detect contradictions between stored facts. Staleness handling — overwrite on update, namespace separation, or a contradiction check — is left to the application layer, as it is with other vector memory systems today.

If you're wiring up memory for an agent that tracks facts which change over time, Feather Core is free and self-hosted (MIT license) if you want to try the update/namespace patterns above against a real store.