BlogResources

Context bleeding: when one customer sees another one's conversation

How state gets shared between people who should never share it, the four places it happens, and the identity problem underneath.

Rebecca PearsonRebecca PearsonUpdated 11 min read

Summarize with AI

Context bleeding: when one customer sees another one's conversation
On this page

To avoid context bleeding in a multi-user WhatsApp bot, key every piece of personal data on the conversation it belongs to, filter message history at the source, and test with several conversations running at the same time. Context bleeding is when one person's conversation state appears in another's: a customer asks about their order and is told about someone else's.

Context bleeding is the worst failure a bot can have, because it discloses one person's information to another. That is a data protection incident however minor the detail seems, and it damages trust in a way a wrong opening hour never will.

What we'll cover

Key takeaways

  • Context bleeding shows up first as small glitches such as a wrong name, and a wrong name is the same defect as a wrong order.
  • The four causes are shared state with no key, a key that is not unique, history fetched from too broad a scope, and phone numbers reassigned to new people.
  • A phone number identifies an account, not a person, so verify identity before disclosing anything sensitive.
  • Sequential testing cannot find this defect, so test with several similar conversations running in parallel and read the assembled model context.
  • If it has already happened, treat it as a data protection incident and not as a bug.

What it looks like in practice

Context bleeding rarely appears as a whole conversation in the wrong place. It usually shows up as five smaller symptoms that are harder to notice.

The wrong name. Customers report it and businesses dismiss it as a glitch.

A detail from elsewhere. Referring to an order, an appointment or a preference belonging to somebody else.

Answering an unasked question, because the model received history from another conversation.

Stale identity, where somebody is treated as a customer from months ago whose number has since been reassigned.

A reply to the wrong person entirely. This is the rare and serious version.

Teams dismiss the first three as the system being odd. They are the same defect as the fifth, caught earlier, and treating them as cosmetic is how the serious one reaches a customer.

The four places it comes from

Shared state with no key. A variable holding "the current order" or "the customer's name" at the level of the application, not the conversation. It works with one user and fails when a second user arrives.

A key that is not unique enough. Keying on something that collides: a name, a session id that gets reused, an order reference two people can both quote.

History assembled from the wrong scope. Building the model's context by fetching recent messages without filtering strictly to this conversation. The model then answers using somebody else's words, faithfully.

Reuse after reassignment. Phone numbers are recycled. An old record attached to a number that now belongs to someone else means greeting a stranger with the previous owner's details.

The first two causes are implementation defects. The third is a design defect and the most common in any bot that uses a model. The fourth is a policy question that you must decide, not code around.

Isolation as the default

Isolation as the default means nothing is shared unless someone decided to share it.

Key everything on the conversation, and make the key explicit instead of implied by whatever happened to be in scope.

Fetch history filtered at the source, not filtered afterwards. Filtering after retrieval means the unfiltered set existed, and something will eventually read it.

Never hold per-user data outside a request in anything long-lived. This defect appears under load and never in testing.

Pass identity explicitly through every layer instead of relying on ambient context. Ambient context is what leaks.

Separate genuinely shared data. Opening hours and prices are shared and should be obviously shared. Anything about a person is not.

The rule in one line: if a piece of data is about a person, attach that person's identifier to it everywhere it travels.

The identity problem underneath

A phone number identifies an account, not a person. Numbers get reassigned, phones are shared within families and small businesses, and someone may message from a work number about a personal matter. Numbers can also be spoofed in other contexts, which matters when the conversation is the only evidence of who you are dealing with.

Three practical consequences follow.

Do not treat the number as proof of identity for anything consequential. Verify separately before disclosing anything sensitive or acting on an instruction that matters.

Age out old associations. A record attached to a number nobody has used in a long time is more likely to be wrong than useful.

Re-confirm instead of assuming. Opening with "Is that Ms Patel?" costs one exchange and prevents the case where it is not.

Building it with CodeWords

CodeWords connects to 3,000+ integrations (CodeWords integrations directory, 2026), and you make each connection once and reuse it.

  1. Open CodeWords and start a new automation.
  2. Describe what should happen in plain language to Cody, the automation builder, including what the bot may say about somebody's existing records and when it must confirm who it is talking to.
  3. Authorize the connections to your systems and your WhatsApp Business Platform account.
  4. Describe the exceptions: an unrecognised number, a number matching a very old record, two conversations from the same person, anything sensitive.
  5. Test with several conversations running at once, not one at a time, because sequential testing hides this defect.

You describe the outcome; Cody builds it, connects it, and deploys it. The Free plan covers light use. Pro costs $39 per month and Business costs $100 per month as usage grows (CodeWords pricing page, 2026); details are on the pricing page.

Concurrency, and the same person twice

Two situations produce the same symptom.

Two people messaging simultaneously. Shared state fails here. The failure does not appear in testing because testing is sequential, and it appears in production as soon as two people write at once.

One person sending two messages quickly. This is common, because people send a thought and then a correction. If the bot processes both at once, they can interleave, and the second can be answered using state the first was halfway through changing.

Handle the second case explicitly: process messages from one conversation in order, and wait briefly to see whether more are coming before replying. This makes the bot feel more attentive and removes a class of confusion.

How long state should live

Conversation state should live only as long as the conversation needs it. State that lives forever is a leak risk and a correctness problem, because a bot that refers to something from four months ago is usually wrong.

Set a lifetime by what the conversation is for. An order in progress can persist for weeks; a general enquiry for hours. When the state expires, the conversation starts cleanly instead of half-remembering.

Also apply a retention policy to stored conversations. Choose a retention period on purpose instead of accumulating history by default, and check the requirements of the data protection law that applies to you. A shorter history is a smaller problem if anything goes wrong. Keep what matters in your own records, not in conversation state.

Testing for something that happens rarely

Test for context bleeding deliberately, because ordinary testing does not find it. Use four checks.

Run conversations in parallel, several at once, with deliberately similar details: two people with the same name, ordering the same item, on the same day.

Send bursts from one conversation without waiting for replies.

Check what the model was given. Log the assembled context and inspect it for anything that does not belong to this conversation. This check finds the most common version of the defect.

Test with a recycled number. Point an old record at a new conversation and confirm the bot does not assume the old identity.

Repeat these checks after every change to how context is assembled, because this defect gets reintroduced.

If it has already happened

If context bleeding has already happened, treat it as an incident, not a bug. Take five steps.

  1. Establish what was disclosed, to whom, and how many people were affected.
  2. Stop the automation if you cannot bound the problem.
  3. Tell the affected people plainly. They have usually seen the message, and a business that says nothing looks worse than one that explains.
  4. Check whether the incident is reportable under your data protection obligations. That depends on what was disclosed, not on how it happened.
  5. Fix the cause, not the symptom. One visible instance usually means others went unnoticed.

Resist treating a single odd message as a glitch.

Making it survive

Key every piece of personal data on the person it belongs to.

Filter history at the source, not afterwards.

Log the assembled context so it can be inspected.

Age out old number associations rather than trusting them indefinitely.

Re-run parallel-conversation tests after any change to context handling.

Limits worth knowing about

A phone number is not an identity, and no amount of care in your code changes that.

Sequential testing cannot find this. The defect needs concurrency to appear.

Model context is the easiest place to leak, because assembling it feels like a retrieval detail and not a boundary.

Longer memory is not better. More retained state means more to leak and more to get wrong.

One visible instance implies others, because most cases are never reported.

What to check first

Check first what the model is actually being given: log it and read it. Read the real assembled context for a real conversation, not what you believe it contains. The most common version of this defect is history fetched with a filter that is slightly too broad, and it stays invisible until someone reads the input.

Two habits make the difference. Test with several conversations running at once, using deliberately similar details, because sequential testing cannot find this defect. And treat a wrong name as the same defect as a wrong order, caught earlier, not as a cosmetic glitch.

Frequently asked questions

What is context bleeding?

Context bleeding is when state from one person's conversation appears in another person's conversation. Examples are a wrong name, a detail from someone else's order, or an answer to a question asked elsewhere. Each case discloses one customer's information to another, which makes it a data protection problem, not only a quality problem.

What causes it?

Four things cause it: shared state with no key, a key that is not unique enough, model context assembled from too broad a scope, and old records attached to reassigned phone numbers. The first two are implementation defects, the third is a design defect, and the fourth is a policy decision you must make.

Why does it not show up in testing?

Testing is sequential, and the defect needs concurrency to appear. A developer sends one message, reads one reply and moves on, so shared state never collides. In production, two people write at the same time almost at once, so the defect appears quickly at any real volume.

Is a phone number enough to identify a customer?

No, a phone number is not enough. Numbers are reassigned, phones are shared, and a number identifies an account, not a person. Verify identity separately before disclosing anything sensitive or acting on an instruction that matters. Opening with a short confirmation such as "Is that Ms Patel?" costs one exchange.

How should conversation history be handled?

Filter history at the source, not after retrieval, and log the assembled context so you can inspect it. Broad retrieval with a later filter means the unfiltered set existed, and something will eventually read it. Key every stored message on the conversation it belongs to, and fetch only that key.

What about two messages sent quickly by the same person?

Process them in order, not at the same time, and wait briefly to see whether more messages are coming. Interleaved processing produces answers based on half-updated state, because the second message can be answered before the first has finished changing the conversation. Ordered processing also makes the bot feel more attentive.

How long should conversation state live?

Keep it as long as the conversation needs it, then expire it. An order in progress can persist for weeks, and a general enquiry for hours. Indefinite state is a leak risk and is usually wrong. When state expires, the next message starts a clean conversation instead of a half-remembered one.

How do I test for it?

Run four checks: run several conversations in parallel with deliberately similar details, send bursts from one conversation without waiting for replies, inspect the assembled model context, and test a recycled number against an old record. Repeat the checks after every change to how context is assembled.

Is a wrong name a serious problem?

Yes, a wrong name is serious. It is the same defect as a wrong order, caught earlier. Dismissing it as cosmetic is how the serious version reaches a customer. Treat each wrong name as evidence that state is shared somewhere, and find where before the next conversation exposes more.

What should I do if it has happened?

Treat it as an incident. Establish what was disclosed and to whom, stop the automation if you cannot bound the problem, tell the affected people plainly, check whether the incident is reportable under your data protection obligations, and fix the cause, not only the single instance you found.

Get started today

Your first workflow is free to build.

Describe what you need. Cody handles the build, the connections, and the deployment.