Which context kinds and attributes a flag can actually target depends on
where the flag is evaluated. A targeting rule, individual target, or percentage
rollout that names a context kind or attribute the evaluation doesn’t carry
— the context falls through to the default rule (or the
off variation). This reference explains how to pick a context that will actually
fire, and how to determine it deterministically from your own code rather than
guessing.
silently never matches
Read this whenever a flag plan involves a targeting rule, an
individual/expiring target, a percentage rollout, or self-targeting —
i.e. any time you pick a contextKind or attribute.
Target only kinds present on the surface where the flag is read. A flag read
in a browser (client-side SDK) can only match kinds your app actually puts in the
client context; a flag read on the server can match whatever the server builds.
A rule on a kind that isn’t in the evaluation context never matches.
A context’s key is not an attribute. Individual targets match the context
key; everything else (email, plan, country, …) is an attribute and must
be a targeting rule. Targeting an email as if it were a key is the single most
common mistake — see Key vs attribute.
Percentage rollouts bucket by one context kind. A ramp needs that kind
present in the evaluation context, or it can’t bucket — see
Rollout bucketing.
Availability is conditional, not global. The same app can carry different
kinds/attributes in different code paths (authenticated vs anonymous, request
handler vs background job). Decide per surface, not once for the whole app.
The context passed to an SDK’s variation call is assembled by your code, and it
differs by where that code runs:
Server-side evaluation. The server builds the context from whatever it has in
scope — often a user/account plus request-scoped data. Kinds and attributes are
whatever your server-side context builder puts in.
Client-side / browser evaluation. The browser only sees the context your app
ships to it (typically at page load, then updated via an identify call). This is
usually a strict subset of what the server has: request-scoped or
server-only kinds are not present, and an anonymous / pre-auth page (login,
signup) may carry no user/account context at all.
Mobile evaluation. Similar to client-side: the app controls the context and
it reflects the signed-in (or anonymous) state on the device.
Implications:
A flag read in both server and client code should target only kinds present
in both contexts.
A client-side flag evaluated on an anonymous / pre-auth page cannot match a
user- or account-scoped rule — plan the fallthrough / off variation for the case
where nothing matches.
Client SDKs can add attributes at runtime (via an identify call) only to a kind
the shipped context already has; they generally can’t conjure a brand-new kind on
a page that never carried it. Don’t plan a rule on a kind the surface never sees.
Every context has exactly one key (its stable identifier) plus any number of
attributes.
Individual / expiring targets match the key. “Target this specific user” means
their context key, not their email or name.
Everything else is a rule on an attribute. To target by email, plan, country,
version, etc., write a targeting rule with a clause on that attribute
(contextKind + attribute + op + values).
The classic trap: a context keyed by an opaque user ID, where email is an
attribute. Adding the email as an individual target matches nothing (the key
is the ID, not the email); email targeting must be a rule on the email
attribute. Confirm your context’s key from the code that builds it before choosing
between an individual target and a rule.
A percentage rollout (and a guarded/progressive ramp) hashes a context to place it
in a bucket, using one context kind — the rollout’s bucketBy / rollout
context kind (defaults to the flag's default kind). That kind must be present in the
evaluation context for the ramp to work.
If a flag is only ever evaluated in a context that lacks the bucketing kind
(e.g. a background job with no user context when the ramp buckets by user), the
ramp can’t place it — the context falls through instead of ramping.
Choose a bucketing kind that is present everywhere the flag is read, and stable
per subject so a given subject stays in the same bucket as the percentage grows.
A rule clause with no explicit contextKind defaults to the SDK’s default kind
(historically user). If your app targets a different kind, always set
contextKind explicitly so a rule doesn’t silently land on the wrong kind and fail
to match.
Don’t guess which kinds/attributes exist — read the code that builds the context:
Find where the context is constructed. Search for the SDK context builder in
your codebase — e.g. LDContext, newContext / ContextBuilder (Go),
LDContext.builder (Java), Context.builder / LDContext (JS/React),
Context.create (Python), or the object literal passed to variation /
useLDClient / identify. That call site is the source of truth for the kinds
and attributes available at that evaluation.
Compare the surfaces. If the flag is read on more than one surface
(server + client, authenticated + anonymous), read each surface’s context
construction and target only what they share.
Confirm the key. Read what value is used as the context key so you know
whether a subject is targetable as an individual target (by key) or only via a
rule (by attribute).
Cross-check a neighbor. Inspect an existing flag’s rules (their contextKind
/ attribute) to confirm the convention already in use, and a rollout’s
bucketing kind to confirm what ramps bucket by.