Poisoning persists in long-lived context (memory, summaries) and influences many future turns; injection lives one call. Defend poisoning at write time, not assembly time.
Imagine a one-shot lie whispered in a meeting can be challenged on the spot and forgotten by tomorrow. A lie written into the official meeting minutes is read by everyone who opens those minutes next week, next month, next year, until someone notices and corrects the record. Prompt injection is the whisper; context poisoning is the lie in the minutes. The agent's persistent memory, its rolling conversation summary, its saved chat snippets, those are the minutes. Once a bad statement lands there, every future session that reads from those minutes inherits it. Stopping it requires guarding what gets written down, not just being careful about what gets said in the moment.
Concept explanation~2 min read
Everything you need to truly understand this topic: intuition, mechanics, step by step explanation, code, formulas, and worked example. Click to expand.
Concept explanation~2 min read
Everything you need to truly understand this topic: intuition, mechanics, step by step explanation, code, formulas, and worked example. Click to expand.
Prompt injection is the well-known failure: a tool result or user input contains hidden instructions, the model treats them as commands, the call goes wrong. Context poisoning is the less-discussed but more dangerous cousin: an injection (or an honest mistake) writes a hostile or incorrect statement into long-lived context, and from then on the agent is wrong on every future turn that reads from that slot.
The two failure modes are related but the defenses do not transfer. This deep dive draws the line cleanly, walks through the attack chain, and lays out the write-time and lifecycle controls that production agent stacks need.
The persistence surface
A one-shot prompt injection lives in the context of a single call. When the call returns, the injection is gone, assuming the call did not write anything persistent. The blast radius is one user, one turn, one outcome.
Context poisoning targets the persistence surfaces of the agent stack. These are any slots whose contents survive past the call:
- User memory stores. Mem0 fact tables, Letta memory blocks, ChatGPT user memory, custom Postgres memory tables.
- Rolling conversation summaries. A summary block that gets re-injected at the top of every future turn in the same session, sometimes across sessions.
- Knowledge graphs. Zep's temporal graph, custom KG stores with extracted facts.
- Vector-indexed past chats. A common pattern is to index prior conversation turns into a vector store for retrieval over history.
- Checkpointed agent state. LangGraph's checkpointer persists state including any scratchpads.
Anything the agent will read on a future turn is in scope. The shared property is that a single write can influence many reads, possibly across many users if the store is shared.
Situations where this technique stops working.
2–4 min · Everything important, quickly.
# Hardened memory write path
from pydantic import BaseModel, Field
from enum import Enum
class Source(str, Enum):
USER_STATED = 'user_stated'
AGENT_INFERRED = 'agent_inferred'
TOOL_DERIVED = 'tool_derived'
class MemoryEntry(BaseModel):
subject: str
predicate: str
object: str
source: Source
call_id: str
confidence: float = Field(ge=0, le=1)
ttl_days: int
INJECTION_PATTERNS = [
'ignore previous instructions', 'as an admin', 'forget that you cannot',
'system override', 'new instructions:', 'pretend you are'
]
def commit_memory(entry: MemoryEntry, store) -> bool:
# 1. Schema validation already enforced by Pydantic
# 2. Adversarial-pattern matching
text = f'{entry.subject} {entry.predicate} {entry.object}'.lower()
if any(p in text for p in INJECTION_PATTERNS):
log_security_event('memory_write_blocked', entry)
return False
# 3. Source-trust gating
if entry.source == Source.TOOL_DERIVED and entry.confidence < 0.7:
entry.ttl_days = min(entry.ttl_days, 1) # short TTL for low-trust
# 4. Provenance + commit
store.write(entry, provenance={'call_id': entry.call_id})
return TrueReal products, models, and research that use this idea.
- ChatGPT memory has documented incidents where injected page content caused the model to write hostile or false personal facts into a user's memory, persisting until the user manually cleared them.
- Mem0 production guidance recommends a schema-validated extractor and source-trust scoring before commit; Letta exposes memory blocks as named, type-checked structures rather than free text.
What an interviewer would ask next. Try answering before peeking at the approach.
QHow would you detect that an agent's memory has been poisoned after the fact?
Run a scheduled LLM-as-judge pass against stored memory looking for adversarial-pattern signatures, contradictions with high-confidence facts, and entries whose provenance is a low-trust tool source above a confidence threshold.
Red flags & common mistakes
The phrases that signal junior thinking. Click to expand.
Red flags & common mistakes
The phrases that signal junior thinking. Click to expand.
Treating poisoning as a flavor of injection and proposing the same defenses. Assembly-time delimiters do nothing for content already saved in memory.
60 second bullets to scan on the way to the call.
The blast-radius difference between a one-call injection and a persisted poisoning
The two-step attack chain: injection writes, persisted memory replays
Primary sources. Browse if you want the original framing.
Same topic, related formats. Practice these next.