Skip to main content
Someone says the interface changed under them, or two people on your team see different things and expect not to, or your server and browser disagree. Assignment is deterministic — the same identity and experiment key always produce the same variant. So a mismatch means the identity changed, not that assignment is random.

identify() was called after the variant rendered

The most common cause by far. Before identify(), a visitor is bucketed on an anonymous id. After it, they’re bucketed on your user id. Different input, different hash, potentially a different variant — so a user who logs in mid-session can watch the page change.
Fix: call identify() as early as you can, before rendering anything under test. In React, that means in an effect that runs as soon as auth resolves, not deep in the component tree. It also skews data: the anonymous exposure and the identified conversion can end up attributed to different arms. Identifying early is a correctness fix, not just a cosmetic one.

The same person on two devices

Anonymous ids live in browser local storage, so they’re per browser. The same human on a laptop and a phone is two participants until they sign in, and can legitimately be in different arms. Once both sessions call identify() with the same user id, both bucket on that id and agree. Nothing can retroactively merge the pre-login history on the second device. Expected behaviour, worth explaining to whoever reported it.

Server and browser disagree

Both sides must bucket on the same identity at the same moment:
  • userId when the visitor is identified
  • otherwise the trevo_id cookie value
A backend that buckets on userId while the browser is still anonymous will assign a different variant to the same visit.
Next.js middleware + getTrevoBootstrap() handle this for you. If you’re hand-rolling with resolveExperiments(), it’s your responsibility.

Local storage was cleared

Clearing storage discards the anonymous id, and a fresh one is minted on the next visit. That’s a new identity, so a new assignment. Incognito windows start fresh every time — which is why testing in incognito can show you a different variant each session. Note this is why AsyncStorage is a required peer for React Native: without persistence the id is re-minted every launch, re-bucketing every user continuously.

Someone is using a forced variant

QA overrides pin a variant for testing:
They persist for the browser tab session. If a teammate is looking at a forced variant, they’ll disagree with everyone else and with their own next session. Clear it with:
Forced variants record no exposure, so they don’t contaminate results — but they do confuse side-by-side comparisons.

Everyone sees control

Different problem — that’s usually not a mismatch at all:
  • The experiment key in your code doesn’t match the one in Trevo. Unknown keys return 'control' and log a warning once
  • The experiment is paused or finished
  • Config hasn’t loaded yet — check trevo.isReady()
  • The SDK never initialised — see No events arriving

Confirming an assignment

Assignment is reproducible, so you can check what a given user should get without guessing. Given their identity and the experiment key, the same hash runs everywhere — including in your own tooling. See the bucketing spec. For a quick manual check in the browser:
Reading it this way doesn’t record an exposure, so support debugging doesn’t pollute results.