What Is Trace State in OpenTelemetry?

What Is Trace State in OpenTelemetry?

A plain-English guide to the small, shared notebook that travels with every traced request — the place where different tracing tools quietly leave notes for themselves and for each other.

Imagine a parcel travelling across the world through the hands of many companies. Every parcel carries a standard label: a tracking number, a sender, and a destination. Every company understands that label. But the companies also want to write their own little notes on the parcel. One writes “fragile, handle with care.” Another writes “customs form attached.” A third writes a sorting code that only its own machines can read. These notes are not part of the standard label, and no company wants the others to erase or misread them.

A request travelling through a modern software system is a lot like that parcel. It passes through many services, and often through tools from several different vendors. The request carries a standard identity so that everyone can agree it belongs to the same trace. Yet each tool sometimes needs to attach a private note to the request. OpenTelemetry provides a tidy place for those notes, and that place is called Trace State.

This guide explains trace state from the ground up. You will learn what it is, what it looks like, how it travels, who is allowed to change it, how it connects to sampling, and how to keep it small and safe. Every new word is explained the first time it appears, so no earlier knowledge of observability is needed.

1What Is Trace State?

The short answer first, then the words that make it clear.

Trace State is a small list of named notes that travels along with a trace from one service to the next. Each note is a key paired with a value, written as two short pieces of text joined by an equals sign. Tracing tools use these notes to carry extra information that the basic trace identity has no room for: a sampling hint, a routing label, a vendor-specific code, or a reference to another system.

Let us unpack the vocabulary. OpenTelemetry, often shortened to OTel, is a free, open source framework, hosted by the Cloud Native Computing Foundation, that gives developers one standard way to produce telemetry: the stream of traces, metrics, and logs that describes how software behaves. A trace follows one request through every service it touches, and it is built from spans, each span being one timed step of the work. Every span carries a small bundle of identity facts called the span context. Trace state is one of the pieces inside that bundle.

The span context holds five things. The trace identity is a long random number shared by every span of the trace. The span identity names this particular step. The trace flags hold tiny switches, the most important being the sampled flag, which says whether the trace is being kept. The remote marker tells whether the context arrived from another program. And the trace state is the optional list of vendor notes. The first three are essential. The trace state is optional and is the only part that is open to extension.

5
Parts of a span context, of which trace state is the only open-ended one
32
Maximum number of notes in a W3C trace state list
0
Notes required: a trace works perfectly with an empty trace state
Everyday Analogy

Think of a hotel guest carrying a key card. The key card has a standard number that every door in the hotel understands. On the back, the spa, the restaurant, and the gym each stamp a small private mark. The doors do not care about the marks, but each business reads its own mark when the guest returns. Trace state is the back of the key card. The front, with the standard number, is the trace identity.

What trace state answers

  • Can tools share extra facts? Yes. Each tool may add its own named note without disturbing the standard fields.
  • Can one tool read another’s note? It can see it, but it should not change it, and it should pass it along unchanged.
  • Is it required? No. Many requests carry an empty trace state, and everything works.
  • Does it carry application data? No. It is meant for tracing-system information, not for business details.
  • Does it travel automatically? Yes, when the standard propagation is configured, it rides in a header next to the main trace header.

What trace state is not

Trace state is not baggage. Baggage is a separate feature that lets your application code attach its own facts, such as a customer tier, so that other services can read them. Trace state is for tracing tools, not for your business logic. Trace state is also not a span attribute. A span attribute is a label stored with one span and sent to the backend for people to read. And trace state is not a place for large or private data. It travels on every network call, so it must stay small and harmless.

i
Remember This

The trace identity says which trace this is. The trace state says what the tools want to remember about it. Delete the trace state and the trace still works. Delete the trace identity and the trace is gone.

A Closer Look at Why It Is a List

You might wonder why trace state is a list of pairs and not one free-form text. The answer is safe sharing. Imagine three vendors that all want to leave a note. If the trace state were one blob of text, each vendor would have to understand the others’ formats, and any careless edit might damage someone else’s note. By making it a list with named keys, each vendor owns its own entry and can change that entry without touching the rest.

This design is a classic solution to a coordination problem. When many independent parties write into one shared place, the safest rule is “every party gets its own labelled slot, and nobody edits another party’s slot.” Trace state follows that rule, and the rest of this guide will show how the small details of the format support it.

Why You Rarely See It, Yet It Matters

Most engineers work for years with tracing and never look at the trace state. The libraries handle it quietly, and no dashboard highlights it. This invisibility is a sign of good design, since a feature that demands constant attention would be a burden. But invisibility also hides risk. When something goes wrong with a vendor feature or with sampling statistics, the trace state is often where the first clue lies. Knowing that it exists, and roughly what it looks like, turns a baffling problem into a short investigation.

2Why Trace State Exists

A standard identity is not enough, and private extras must not break the standard.

In the early days of distributed tracing, every tracing product invented its own headers. A request that passed through a product from one vendor and then a product from another often lost its trace, because the second product did not understand the first one’s header. Companies that wanted to mix tools were forced to choose one, or to write awkward translation code.

The web standards organization, the W3C, solved the core problem by publishing the Trace Context standard. It defines a common header, traceparent, that carries the trace identity, the span identity, and the flags in one fixed format. With this, tools from any vendor can join the same trace. But the standard header was deliberately kept small and strict. It had no room for vendor-specific extras, and yet vendors still had real needs: to pass along their own sampling hints, their own routing information, and their own correlation codes.

The standard therefore added a second header, tracestate. It is the official place for vendor-specific extras, and it comes with rules that keep one vendor’s notes from damaging another’s. OpenTelemetry adopted both headers as its default way of passing trace context, and exposes the trace state as part of its span context.

Everyday Analogy

Imagine an international postal treaty. The treaty says every parcel must carry a standard label in a fixed format, so any country’s post office can handle it. That is traceparent. The treaty also says every post office may add its own sticker on a reserved corner, and others must leave it alone. That is tracestate. Without the first rule there is chaos. Without the second rule, nobody can innovate.

A short history

1

Private headers everywhere

Each tracing product used its own headers, and a trace broke whenever a request crossed from one product to another.

2

Attempts at common formats

Open projects introduced shared header styles, but they differed from each other, and multi-vendor setups still needed translators.

3

The W3C Trace Context standard

The W3C defined two headers: a strict one for identity and a flexible one, tracestate, for vendor extras, with rules for sharing the flexible one safely.

4

OpenTelemetry adopts it

OpenTelemetry made the W3C headers its default and defined the trace state as a standard part of the span context, with simple operations to read and change it.

5

Newer uses

Work on consistent probability sampling uses the trace state to carry sampling information that all services can agree on, which shows that the open design continues to find new jobs.

i
The Core Insight

A good standard is strict where agreement is essential and flexible where innovation is needed. The trace identity is the strict part. The trace state is the flexible part, and the rules about its format are what keep flexibility from turning into chaos.

The Problem Before Standards

Before a shared format existed, a company that wanted to mix two tracing products faced an uncomfortable choice. It could ask both vendors to build converters, which was slow and fragile. It could write its own translation layer, which was costly to maintain. Or it could standardise on one vendor and give up the benefits of the other. Each option locked the company in or slowed it down. A shared header for identity, plus a shared and safe place for private extras, removed this lock-in and let companies choose tools on their merits.

3Core Concepts You Need First

Eight small ideas hold up the whole topic. Learn these and the rest will feel natural.

Idea 1

Span context

The small bundle of identity facts that travels with a span: trace identity, span identity, flags, remote marker, and trace state. It is the passport of the request. Trace state is one page of that passport.

Idea 2

Key and value

Each note has a short name, the key, and a short piece of text, the value. For example, a key might be a vendor’s short name, and its value might be a code only that vendor understands.

Idea 3

Vendor

A company or project that provides a tracing tool. Each vendor owns the keys it creates, and other vendors should not alter them. Even a team inside your own company can act as a vendor.

Idea 4

Propagation

The act of carrying context from one service to the next, usually inside headers of a network request. Trace state travels by propagation, together with the trace identity.

Idea 5

Header

A line of extra information attached to a network request, like the writing on the outside of an envelope. The standard defines a header named traceparent and another named tracestate.

Idea 6

Immutable

A word meaning “cannot be changed once made.” Trace state is immutable: to change it, you make a new copy with the change applied. This avoids accidental edits shared between threads.

Idea 7

Sampler

The small piece of code that decides whether a trace is kept. It can read the incoming trace state and may add its own note, which is how sampling information can travel to later services.

Idea 8

Propagator

The piece of the library that writes the context into outgoing headers and reads it from incoming headers. The default propagator handles both the identity header and the trace state header.

How the ideas fit together

Here is the whole story in one breath. A request arrives at a service, carrying two headers. The propagator reads them and builds a span context that includes the trace state. The sampler may look at the trace state and may add or change a note. The new span carries the resulting trace state. When the service calls another service, the propagator writes the trace identity and the trace state into the outgoing headers, and the cycle repeats.

Everyday Analogy

Picture a relay race in which each runner carries a baton with a standard number engraved on it, and a small notepad strapped to the side. Runners may jot a note on the notepad before handing the baton on, but they must not scribble out earlier notes. The baton number is the trace identity. The notepad is the trace state. The handover is propagation.

Why immutability matters

Programs often do many things at once. If trace state could be changed in place, two parts of a program might change it at the same time and corrupt each other’s work. By making trace state immutable, OpenTelemetry guarantees that any copy you hold is stable. When you add, update, or delete a note, you receive a brand-new trace state, and the old one is left alone. Everyone holding the old copy continues to see exactly what they saw before.

i
A Helpful Mental Model

Treat trace state like a printed card. If you want a different card, you print a new one. You never scribble on the card that someone else is holding.

Who Owns What

Ownership is the key to every rule in this guide. The tracing library owns the trace identity, the span identity, and the flags. Each vendor tool owns the single entry that carries its own key. The sampler owns any sampling notes. Your application code usually owns nothing in the trace state at all. When every party sticks to what it owns, the whole system works without negotiation. When one party starts changing what it does not own, subtle bugs appear that are hard to trace to their source.

4The Anatomy of Trace State

The format is simple on purpose. A few small rules make it safe for many tools to share.

When trace state is written into a network header, it becomes a single line of text. The line is a list of notes separated by commas. Each note is a key, an equals sign, and a value. A short example, from the W3C standard itself, looks like this in words: the key “congo” paired with the value “t61rcWkgMzE,” followed by a comma, followed by the key “rojo” paired with the value “00f067aa0ba902b7.” The two names are made-up vendors used in the standard’s examples.

PieceExampleWhat it means
KeycongoThe name of the note, normally identifying the vendor or system that owns it
Valuet61rcWkgMzEThe content of the note, understood only by its owner
One entrycongo=t61rcWkgMzEA key and value joined by an equals sign
The listrojo=00f067aa0ba902b7,congo=t61rcWkgMzEEntries separated by commas, with the most recently changed entry first

The rules for keys

Keys must be written in lowercase letters, digits, and a few simple symbols, and they must begin with a letter or a digit. A key can also take a two-part form for platforms that host several customers: a tenant name, an at-sign, and a vendor name. For example, a hosting platform might use a key made from the customer’s name, an at-sign, and the platform’s own name. This way, one vendor can serve many customers without their notes colliding. Keys have a length limit of 256 characters in the simple form, and the standard sets tighter limits on the two halves of the tenant form.

The rules for values

Values are plain printable text. They may not contain commas, because commas separate entries, and they may not contain equals signs, because equals signs separate keys from values. They may not end with a space, and they have a limit of 256 characters. These rules keep the line easy to split and easy to read by any program, without special quoting or escaping.

RuleThe limitWhy it exists
Number of entriesAt most 32Keeps the header from growing without bound
Length of a keyUp to 256 characters, with tighter limits in the tenant formKeeps parsing fast and memory use predictable
Length of a valueUp to 256 charactersPrevents one vendor from filling the whole header
Characters in a keyLowercase letters, digits, and a few symbolsAvoids confusion over capital letters and odd characters
Characters in a valuePrintable text without commas or equals signsLets the line be split reliably
Unique keysEach key appears at most oncePrevents ambiguity about which value is correct
OrderMost recently updated entry firstLets receivers see who touched it last
Everyday Analogy

Think of a shared noticeboard with a few strict house rules. Notes must be on small cards, each card signed with the writer’s name, no more than thirty-two cards at a time, and the freshest card pinned on the left. The rules are boring, but they mean that fifty different people can use the board without a fight.

What happens when the rules are broken

If a trace state header does not follow the rules, for example because it has duplicate keys, a bad character, or too many entries, the standard says that a receiving tool should discard the whole trace state and start with an empty one, rather than try to repair it. The trace identity, carried by the other header, is unaffected. This is a good example of “failing safe”: a damaged note is dropped, and the trace continues.

!
A Bad Note Can Cost You All Notes

Because a malformed trace state is thrown away entirely, one careless tool that writes an invalid entry can erase the valid notes of every other tool. Always follow the character and length rules exactly.

Reading a Real Example Slowly

Take the list with two entries: first rojo with the value 00f067aa0ba902b7, then congo with the value t61rcWkgMzE. Reading from the left, the first entry is the most recently changed, so the vendor named rojo touched the trace last. The second entry was written earlier by congo and has stayed the same since. Neither value means anything to a human reader, and that is fine: each is a private code that only its owner understands. A third party looking at this list can learn only the names of the tools involved and the order in which they acted.

5Trace State Beside Its Sister Header

Two headers travel together. One is essential, and one is a bonus.

The traceparent header holds four parts: a version number, the trace identity, the identity of the parent span, and the flags. It has a fixed length and a fixed format, and every standard-following tool must understand it. The tracestate header holds the list of vendor notes. It is optional and variable in length.

Featuretraceparenttracestate
PurposeCarries the essential identity of the traceCarries optional vendor-specific notes
Required?Yes, without it there is no trace contextNo, it may be absent or empty
FormatFixed four-part layoutFlexible list of key and value pairs
SizeFixed and smallVariable, up to the limits
Who may change itThe tracing library, following the standardEach vendor may change only its own entries
If it is invalidThe receiver starts a new traceThe receiver discards it and carries on
Depends on the other?No, it stands aloneYes, it is ignored when traceparent is missing or invalid

The most important row is the last one. Trace state has meaning only in the company of a valid traceparent. If the main header is missing or damaged, the receiver begins a new trace, and any trace state that came with the bad header is discarded as well. This is natural: notes about a trace make no sense if the trace itself cannot be identified.

Everyday Analogy

A train ticket has the traveller’s name, date, and seat in a fixed layout that any conductor can read. A traveller may also carry a handwritten note from a friend tucked behind the ticket. If the ticket is torn and cannot be read, the conductor does not bother with the note. The ticket is the traceparent, and the note is the tracestate.

A note on the sampled flag

The sampled flag lives in the traceparent header, not in the trace state. When people say “the sampling decision travels in the header,” they mean this flag. The trace state can carry additional sampling facts, such as the probability with which the trace was chosen, but the yes-or-no mark itself is part of the main header. We will see later how these two work together.

i
Two Lines, One Journey

When you look at a request in a debugging tool, you will often see the two headers side by side. Read the first for who the trace is. Read the second for what the tools have added.

Versioning and Future Growth

The identity header begins with a version number, which gives the standard room to grow without breaking old tools. The trace state has no version, because it is already designed to be extensible: new vendors simply add new keys. A tool that does not recognise a key keeps it and passes it on. This forward-looking design means that new features can appear in some parts of a system without forcing every other part to be upgraded first, which is exactly what you want in a large organisation where teams move at different speeds.

6How Trace State Travels Between Services

The notes ride along on every network call, and each service has a clear set of duties.

When one service calls another, the tracing library takes the current span context and writes it into the headers of the outgoing request. This is called injection. When a service receives a request, the library reads the headers and rebuilds the span context. This is called extraction. Both steps are performed by the propagator. The default propagator in OpenTelemetry follows the W3C standard, so both headers are handled together without any extra work from the programmer.

sequenceDiagram
    participant G as Gateway
    participant C as Checkout
    participant P as Payment
    G->>C: traceparent flag 01 and tracestate congo=t61rcWkgMzE
    Note over C: Reads both headers and builds the span context
    Note over C: A vendor tool adds its own entry on the left
    C->>P: traceparent flag 01 and tracestate rojo=00f067aa0ba902b7,congo=t61rcWkgMzE
    Note over P: Passes the unknown entries along unchanged
    P-->>C: Reply
    C-->>G: Reply
Figure 1: Trace state is read, possibly extended, and passed along at every hop

Explaining Figure 1

Read the diagram from top to bottom. The gateway calls checkout and writes two headers: the identity header with its sampled flag, and the trace state, which here holds one note from the vendor named congo. Checkout extracts both. A tracing tool from a second vendor, named rojo, adds its own note, and the library places it at the left of the list. When checkout calls payment, it writes the longer list. Payment does not recognise either vendor, but it knows the rule: pass unknown notes along unchanged. The notes therefore reach every later hop intact.

The three duties of a service

1

Extract

Read both headers from the incoming request and rebuild the span context, including the trace state. If the headers are invalid, begin a new trace.

2

Keep

Hold the trace state in the span context while the request is being handled. It is immutable, so nothing can alter it by accident.

3

Inject

When calling another service, write the identity and the trace state into the outgoing headers. Update only your own entries, and leave everyone else’s untouched.

Everyday Analogy

Think of a suitcase with luggage tags from several airlines. Each airline adds its own tag and reads only its own. When the bag changes planes, the ground crew moves the bag, tags and all, without peeling off any tag that is not theirs. Injection, extraction, and passing along unchanged are the ground crew’s duties.

Services that do not understand trace state

A service that does not use any vendor tool still has an important job. If it uses the standard propagator, it will extract the trace state and inject it again on outgoing calls, even though it never looks inside. This “pass-through” behaviour is essential. If a middle service dropped the trace state, vendors on either side of it would lose their notes and might behave incorrectly. Make sure every service in a chain, including proxies and gateways, forwards both headers.

!
Do Not Forget the Middle

Proxies, load balancers, and message queues are common places where headers get lost. A lost tracestate does not break the trace, so the loss can go unnoticed for a long time, while vendor features that depend on the notes quietly stop working.

Asynchronous Work and Queues

Not every call is a direct request and reply. A service may place a message on a queue, and another service may pick it up minutes later. The headers of a network call do not exist in this case, so the trace context, including the trace state, must be copied into the message’s own properties and read back by the consumer. Libraries for popular queues often do this automatically, but older or custom code may not. When the context is not copied, the consumer starts a fresh trace and the notes are lost, which is why queues appear again and again in troubleshooting stories.

7Changing Trace State: Add, Update, Delete

Because trace state is immutable, every change produces a new copy. Four simple operations cover everything.

OpenTelemetry offers a small set of operations on the trace state. You can get the value of a key, add a new entry, update the value of an existing entry, and delete an entry. The three that change something, add, update, and delete, never alter the original. Each one returns a new trace state with the change applied, and the original stays exactly as it was.

flowchart TD
    A["Existing trace state: congo=t61rcWkgMzE"] --> B{"Which operation?"}
    B -- "Get" --> C["Read the value; nothing changes"]
    B -- "Add a new key" --> D["New copy: new entry placed at the left"]
    B -- "Update an existing key" --> E["New copy: entry changed and moved to the left"]
    B -- "Delete a key" --> F["New copy: entry removed"]
    D --> G["Original is left untouched"]
    E --> G
    F --> G
Figure 2: Every change makes a new copy and leaves the original alone

Explaining Figure 2

The diagram starts with an existing trace state holding one note. If the program only wants to read a value, the Get operation returns it and nothing changes. The other three operations each lead to a new copy. Adding places the new entry at the left of the list. Updating changes an existing entry and also moves it to the left. Deleting removes the entry. In every case, the original trace state remains untouched, shown at the bottom of the diagram.

Why the newest goes to the left

The standard asks that the most recently modified entry sit at the left end of the list. This small habit tells a reader which vendor touched the trace last. It also helps when the list grows too long: if a tool must shorten the list to fit the limits, the entries at the right, which have been unchanged the longest, are the ones that can be dropped first with the least damage.

OperationStarting listResultPlain meaning
Add rojocongo=t61rcWkgMzErojo=00f067aa0ba902b7,congo=t61rcWkgMzEA new vendor leaves a note, placed first
Update congorojo=00f067aa0ba902b7,congo=t61rcWkgMzEcongo=newvalue,rojo=00f067aa0ba902b7The vendor revises its note, and it moves to the front
Delete rojorojo=00f067aa0ba902b7,congo=t61rcWkgMzEcongo=t61rcWkgMzEA vendor removes its own note
Get congorojo=00f067aa0ba902b7,congo=t61rcWkgMzEt61rcWkgMzEReading only; no new copy
Everyday Analogy

Imagine a shared recipe card that nobody is allowed to scribble on. Whenever someone wants to add an ingredient, they photocopy the card, write the change on the copy, and hand the copy on. The original card in the drawer stays clean. Anyone who was reading the original is not startled by sudden changes.

The golden rule: only touch your own entries

Each vendor should add, update, or delete only the entries whose keys it owns. It should leave every other entry exactly as it found it, including the order of the remaining entries. This rule is what makes the shared list safe. A vendor that rewrites everything, or that deletes entries it does not recognise, can break features in other tools without ever knowing it.

There are a few cases where a vendor must make room. If the list would exceed 32 entries, the standard allows a tool to remove entries from the right end, preferably the longest ones first, since they cost the most space. Even then, a tool should avoid removing entries unless absolutely necessary, and it should never reorder entries it does not own.

i
Application Code Rarely Touches It

In everyday programs, trace state is changed by the libraries and vendor tools, not by business code. If you ever do change it yourself, follow the same golden rule: use a clear key that you own, keep the value short, and never touch anyone else’s entries.

What a Typical Day of Changes Looks Like

On a normal day, in a normal system, the trace state is changed far less often than you might fear. Most requests arrive with an empty list or a list that a vendor wrote at the entrance. Most services forward it without touching it. One or two components, a monitoring tool or a sampler, may add or update a single entry. By the time the request completes, the list has changed once or twice, and each change was made by the owner of the entry. This calm pattern is what the rules are designed to produce.

8Trace State and Sampling

The sampler can read the notes and add its own, which lets sampling facts travel with the trace.

Sampling means keeping only a portion of the traces, because recording everything is expensive. A sampler is the small piece of code that decides, each time a span is about to start, whether the trace will be kept. If you have read about ParentBased sampling, you know that the yes-or-no decision travels in the sampled flag of the main header. Trace state adds a second channel, one that can carry more detail about how and why the decision was made.

When a sampler is called, it receives the parent’s span context, which includes the incoming trace state. When it answers, it returns three things: the decision, any extra attributes to add to the span, and a trace state. The trace state it returns becomes the trace state of the new span, and from there it is injected into outgoing calls. A sampler may return the trace state unchanged, which is the most common case, or it may add or update an entry of its own.

flowchart TD
    A["Request arrives with traceparent and tracestate"] --> B["Propagator extracts the span context"]
    B --> C["Span context includes the incoming trace state"]
    C --> D["Sampler is asked: keep this span?"]
    D --> E["Sampler returns decision and a trace state"]
    E --> F{"Did the sampler change the trace state?"}
    F -- "No" --> G["New span carries the incoming trace state"]
    F -- "Yes" --> H["New span carries the updated trace state"]
    G --> I["Propagator injects it into outgoing calls"]
    H --> I
Figure 3: The sampler reads the incoming trace state and decides what the next services will see

Explaining Figure 3

The diagram follows a request through one service. The propagator rebuilds the span context, including the trace state, from the incoming headers. The sampler is then asked whether to keep the span, and it hands back both its decision and a trace state. If it left the trace state alone, the new span simply carries what arrived. If it added or changed a note, the new span carries the updated version. Either way, the result is written into the headers of outgoing calls, so downstream services see the sampler’s notes.

Why sampling facts are worth carrying

Consider a backend that wants to estimate how many requests really happened by looking only at the traces that were kept. If one trace in ten is kept, each kept trace stands for about ten real requests. But the backend can only multiply correctly if it knows the probability with which each trace was chosen. If different services use different rates, or if the rate changes during the day, the backend needs that information attached to each trace. The trace state is a natural place to carry it.

Here is a small example. Suppose 1,000 traces were kept at a probability of ten percent. The backend estimates 1,000 ÷ 0.10 = 10,000 real requests. If the probability had been five percent, the same 1,000 traces would stand for 1,000 ÷ 0.05 = 20,000 requests. Without the probability, the backend cannot tell these two cases apart, and its counts will be wrong by a factor of two.

1,000
Traces kept by sampling
10,000
Estimated real requests at a ten percent probability (1,000 ÷ 0.10)
20,000
Estimated real requests at a five percent probability (1,000 ÷ 0.05)
Everyday Analogy

Imagine a pollster who interviews a sample of voters. To report the result for the whole country, she must know what fraction of voters she sampled. A note on each interview sheet saying “one in a thousand” allows anybody to scale up correctly. The trace state is that note, and without it the sheets would be hard to interpret.

Consistent probability sampling

OpenTelemetry has been developing a newer approach, often called consistent probability sampling, that uses the trace state for exactly this purpose. It reserves a short vendor key for OpenTelemetry itself, the two-letter key “ot,” and places inside it small sub-values, such as a compact threshold that says how likely the trace was to be chosen, and, when needed, an explicit randomness value that all services can use to make the same decision from the same random number. The benefit is that services using different probabilities can still make decisions that fit together: a trace chosen at a lower probability is always a subset of what a higher probability would have chosen. The details of the encoding are still evolving, so check the current specification before building anything that depends on them.

!
Do Not Overwrite the ot Entry

If the “ot” entry carries sampling information, only the sampling machinery should change it. Other tools must pass it along unchanged, or the sampling arithmetic in the backend will quietly go wrong.

What ParentBased does with the trace state

The ParentBased sampler, which follows the parent’s decision, normally leaves the incoming trace state alone and passes it on. This is exactly what you want in the common case: the root of the trace made the decision and wrote any notes, and every later service simply honours the decision and forwards the notes.

Two Channels, Two Jobs

It helps to remember that the sampled flag and the sampling notes in the trace state do different jobs. The flag answers a simple question that every service must be able to answer instantly: should I keep this? The notes answer a more subtle question that only the backend needs to ask later: with what probability was this chosen? Keeping the two apart means that even a tool that knows nothing about trace state can still respect the sampling decision correctly, while advanced tools gain extra information when it is available.

9Trace State, Baggage, Attributes, and Resources

Four different places can hold a fact. Choosing the right one avoids confusion and cost.

Beginners often ask why OpenTelemetry has so many ways to attach information. The answer is that each one has a different audience and a different lifetime. Putting a fact in the wrong place either hides it from the people who need it or sends it to people who should not see it.

WhereWhat it is forWho reads itHow far it travels
Trace stateExtra notes for tracing toolsTracing libraries and vendor toolsAcross every service call, in the tracestate header
BaggageApplication facts that other services need, such as a customer tierApplication code in any serviceAcross every service call, in the baggage header
Span attributesDetails about one operation, such as a routePeople and tools reading the stored traceOnly to the backend, not to other services
Resource attributesFacts about the program itself, such as its name and versionPeople and tools reading the stored dataOnly to the backend, shared by everything the program sends
Trace identity and flagsStandard identity and the sampled switchEvery tracing toolAcross every service call, in the traceparent header
flowchart LR
    R["One request between services"] --> H1["traceparent header"]
    R --> H2["tracestate header"]
    R --> H3["baggage header"]
    H1 --> A["Every tracing tool: identity and sampled flag"]
    H2 --> B["Vendor tools and samplers: private notes"]
    H3 --> C["Application code: business facts"]
    S["Span attributes and resource attributes"] --> D["Sent only to the backend, never to the next service"]
Figure 4: Three headers travel with the request, each with its own audience, while attributes go only to the backend

Explaining Figure 4

The diagram shows one request leaving a service with three headers attached. The identity header is read by every tracing tool. The trace state header is read by vendor tools and samplers. The baggage header is read by application code. Notice the separate box at the bottom: span attributes and resource attributes never ride along on the request. They go straight to the backend with the exported data. This is why an attribute is the right choice for anything that only people need to read, and trace state or baggage is the right choice only when another service must see the fact during the request.

Everyday Analogy

Think of a parcel again. The shipping label is for every handler: that is the trace identity. A sticker with a sorting code is for the courier company’s machines: that is trace state. A letter tucked inside, addressed to the recipient, is for the person at the end: that is baggage. The paperwork in the sender’s office, describing what was sent and when, never travels with the parcel at all: those are the attributes.

A simple decision guide

  • Does a tracing tool need it during the request? Use trace state, and keep it tiny.
  • Does your own application code in another service need it? Use baggage, with care about what you reveal.
  • Do only people reading the trace need it? Use a span attribute.
  • Is it a fact about the program or machine? Use a resource attribute.
i
The Cheapest Place Wins

Anything carried in headers is paid for on every network call. Anything stored as an attribute is paid for once per span. When a fact can live in an attribute, put it there, and use trace state or baggage only when it truly has to travel.

Mixing Them Up Is a Common Beginner Mistake

New users often put a customer identifier in trace state because it travels across services, and then discover that their application code cannot easily read it, because the trace state is meant for tracing tools. Others put technical notes in baggage, where every service’s code can see them, and then discover that the notes leak to outside companies. The decision guide above exists to prevent both of these. Spend a minute with it each time you are tempted to attach a new fact to a request.

10Trace State in Exported Data and Backends

The notes do not vanish when a span ends. They are exported with the span so that backends can use them.

When a span finishes, the SDK hands it to an exporter, which sends it to a Collector or directly to a backend. The standard wire format used by OpenTelemetry, called OTLP, includes a field for the span’s trace state. The field holds the same text that would appear in the header: a comma-separated list of entries. The spans of a trace therefore carry the trace state they had at the moment they were created.

Links, which connect one trace to another, also carry a trace state of their own, because a link points to another span context, which may have its own notes. This lets a backend understand not only which trace a link refers to, but also what the sender knew about it.

What backends do with it

UseHow the backend uses the notes
Estimating countsReads sampling probability notes to scale up the number of kept traces to estimated real traffic
Vendor featuresA vendor’s own tool reads the entry it wrote, for example to link a trace to its own internal record
Correlating systemsMatches a trace with a record in another product by using a shared reference stored in a note
Debugging propagationEngineers look at the trace state of each span to see which tools touched the trace and in which order
Policy decisionsA Collector’s tail sampler or filter may read a note as an input to its rules

Most tracing screens do not show the trace state to ordinary users. It is an engineering detail, something you inspect when something looks odd. Yet it can be a precious clue. If a trace appears broken or a vendor feature does not work, the trace state of consecutive spans often reveals which hop lost or damaged the notes.

Everyday Analogy

A detective examining a suitcase looks at the airline tags to reconstruct its journey. A missing tag tells her where the bag was handled by someone who did not respect the rules. The trace state on each span is the set of tags on the suitcase at each stop.

Collectors and trace state

The OpenTelemetry Collector passes the trace state along with the spans it receives. Processors can read it, and some can change it, though most do not. If you build rules in a Collector that depend on a note, make sure that every upstream service writes it consistently, or the rule will behave unpredictably on traces that lack the note.

i
A Debugging Trick

When a vendor feature misbehaves, compare the trace state on the first span of a trace with the trace state on a later span. If an entry that was present at the start has vanished, you have found the hop that dropped it.

Why Backends Like Having It

For the people who build tracing backends, the trace state in exported spans is a gift. Without it, a backend that wanted to know how a trace was sampled, or which tool had touched it, would have to guess or to ask the application through some separate channel. With the notes stored on each span, the backend can answer these questions directly from the data it already has. This is the reason vendor tools bother to write notes at all: the notes are a bridge between what happens inside a request and what the backend can learn afterwards.

11Size, Cost, and Performance

A few extra bytes on every call add up faster than you would think.

Trace state travels in a header on every network call, so its size is multiplied by the number of calls. Suppose a system makes 50,000,000 service-to-service calls per day, and each call carries a trace state of 200 bytes. The total is 50,000,000 × 200 = 10,000,000,000 bytes, which is 10 gigabytes of extra network traffic per day, just for notes. If a careless tool doubles the size to 400 bytes, the cost doubles to 20 gigabytes.

10 GB
Extra traffic per day from a 200-byte trace state on 50,000,000 calls
20 GB
The same system if the trace state grows to 400 bytes
< 512
A sensible byte budget for the whole trace state

The standard’s limits allow a trace state to become very large in theory: up to 32 entries, each with a key of up to 256 characters and a value of up to 256 characters. That is more than 16,000 bytes. Many web servers and proxies commonly limit the size of a single header to around 8 kilobytes, so a trace state near the theoretical maximum could cause requests to be rejected. In practice, nobody should come close to the limits. A budget of a few hundred bytes for the whole list is a healthy target.

SizeTypical situationAdvice
EmptyNo vendor tool adds notesPerfectly fine, and the cheapest case
Up to about 100 bytesOne or two small notes, such as a sampling hintHealthy
100 to 500 bytesSeveral vendors each adding a noteAcceptable, but review what each note is for
More than 500 bytesMany vendors or large valuesInvestigate and remove what you do not need
Near 8 kilobytesA runaway tool is appending without limitFix it now; requests may already be failing
Everyday Analogy

Think of the weight allowance on a parcel. Each extra sticker weighs almost nothing, but if a million parcels each carry an extra gram, the postal service must carry an extra tonne. A tiny note, multiplied by a huge number of calls, becomes a real expense.

Performance of parsing

Parsing the header takes a small amount of time on every request, in every service. A short, well-formed list costs almost nothing. A long list with many entries takes more time and memory, and a malformed one is parsed and then thrown away. These costs are small for each call but add up for services handling thousands of requests per second. Keeping the list short keeps this cost invisible.

!
Never Append Without a Limit

A tool that adds a new entry on every hop, without ever removing old ones, will grow the header until something breaks. Each vendor should update its own single entry, not append a fresh one each time.

Estimating Before You Adopt a New Tool

Before adding a new vendor tool, ask two simple questions. How many bytes will its entry add to the header, and how many calls does your system make per day? Multiply the two numbers to learn the daily cost. A tool whose entry is 40 bytes on 10,000,000 calls adds 400,000,000 bytes, which is about 0.4 gigabytes per day. A tool whose entry is 400 bytes adds ten times as much. This quick arithmetic often reveals which tools are cheap to adopt and which deserve a closer look.

12Security and Privacy for Trace State

The header travels across networks, sometimes to other companies. Treat it like a postcard, not a sealed letter.

Because trace state rides on every call, it also travels wherever your calls go. When your service calls a third-party application programming interface, the same headers can be sent to that outside company. When a customer’s browser or a partner’s program calls you, it can send any headers it likes. Both directions deserve a thought.

RiskExampleProtection
Sensitive data in notesA tool puts a customer name or an account number into a valueNever place personal or secret data in trace state; use opaque, meaningless codes
Leaking to outside companiesHeaders are copied into calls to a third-party serviceStrip or limit trace headers on calls that leave your trust boundary
Oversized headers from outsidersA client sends a huge header to waste resources or cause failuresEnforce size limits at the edge and reject malformed headers
Crafted entriesAn outsider writes an entry that a vendor tool treats as an instruction, for example to force samplingTreat incoming notes from untrusted callers as untrusted input; ignore or remove them at the edge
Spoofed vendor keysSomeone writes a key that looks like it belongs to your toolValidate values before acting on them, and strip unknown keys at the edge
Information about your structureValues reveal internal host names or system layoutUse opaque identifiers, and review what each tool writes
Header injectionSpecial characters used to smuggle extra headersFollow the character rules strictly, and use well-tested libraries
flowchart TD
    X["Request arrives from outside your network"] --> S{"Is the header size within your limit?"}
    S -- "No" --> R["Reject or drop the trace state"]
    S -- "Yes" --> V{"Does it follow the format rules?"}
    V -- "No" --> R
    V -- "Yes" --> T{"Is the caller a trusted partner?"}
    T -- "Yes" --> K["Keep the entries you recognise"]
    T -- "No" --> C["Clear the trace state and start fresh"]
    K --> P["Process the request"]
    C --> P
Figure 5: A checkpoint at the edge protects the inside from oversized, malformed, or untrusted notes

Explaining Figure 5

The diagram shows a checkpoint at the edge of your network. A request arrives, and the first question is about size. If the header is too big, the trace state is rejected or dropped. If the size is acceptable, the next question is whether the format rules are followed. If not, the trace state is dropped again. Only well-formed, reasonably sized trace state moves on to the third question: is the caller trusted? A trusted partner may keep its notes, which lets partners cooperate. An unknown caller has its notes cleared, and the system starts with a fresh, empty list.

Everyday Analogy

Think of a building’s reception desk. Visitors may arrive carrying all sorts of badges and notes. The receptionist checks that nothing is oversized or suspicious, and then accepts the badges of known partners, while issuing a plain visitor pass to strangers. The inside of the building never has to trust a badge it did not check.

Do not use trace state as an access control

Trace state is not a secure channel. Anyone who can send a request can set any entries they like, and anyone who can see the network can read them. Never decide who may do what based on a value in the trace state, and never place a secret there. It is a place for convenience notes, not for authorisation.

!
Postcards, Not Letters

If you would be uncomfortable seeing a value printed on a postcard that passes through many hands, do not put it in trace state.

Partners and Trust

Not every outside caller is an enemy. Many companies integrate closely with partners and want traces to continue across the boundary. In that case, agree in advance on which keys each side may write and what the values mean, and treat the partner’s notes as trusted only for those keys. Everything else is cleared at the checkpoint. A written agreement of two or three lines is enough, and it turns an informal habit into a safe, documented arrangement that survives staff changes on both sides.

13Monitoring and Troubleshooting Trace State

Trace state fails quietly. A lost note rarely causes an error; it simply stops a feature from working.

Because the trace still works without trace state, problems are invisible to ordinary health checks. A vendor feature may silently degrade, or sampling arithmetic may drift, and nobody sees a failure message. You must look for problems on purpose, using a few simple checks.

SymptomLikely causeWhat to check
A vendor note disappears after some hopA proxy, queue, or library does not forward the tracestate headerCompare the trace state of spans before and after each hop
All notes disappear at onceA malformed entry caused the receiver to discard the whole listLook for illegal characters, duplicate keys, or too many entries
Header-too-large errorsA tool appends entries without limitMeasure the size of the trace state at each hop, and find the tool that grows it
Wrong estimated counts in the backendSampling notes are missing, overwritten, or inconsistentCheck that only the sampler writes the sampling entry, and that it is forwarded unchanged
Vendor feature works for some traces onlySome entry points do not write the noteFind the services that start traces and check their configuration
Notes from unknown keys appearAn outside caller is sending crafted headersApply the edge checkpoint and clear untrusted notes

Check after every change

1

Send one test request through the full path

Choose a request you can recognise, so you can find its trace easily.

2

Look at the trace state on the first span

Confirm it contains the entries you expect and nothing else.

3

Compare it with the last span

Every entry that should survive the journey must still be present, in a sensible order.

4

Measure the size

Check that the total stays inside your budget and does not grow from hop to hop.

5

Try a hostile test

Send a request with an oversized or malformed header from outside, and confirm that the edge checkpoint handles it.

Everyday Analogy

Think of a courier who tracks each parcel at every depot. If the parcel’s special-handling sticker disappears between depots two and three, the courier does not need to search every depot. A glance at the parcel at each stop shows exactly where the sticker was lost.

i
Make It Automatic

Add the end-to-end test to your deployment process. A thirty-second automatic check that confirms an expected note survives the whole path can prevent months of silent degradation.

Keep a Known-Good Sample

One of the most useful troubleshooting tools is a saved example of a healthy trace state, captured when everything was working. When a problem appears, compare the current header with the saved example, and any difference jumps out: a missing entry, a changed order, an unusual size. Store the sample next to your documentation and refresh it after intentional changes. Teams that do this fix mysterious header problems in minutes instead of days.

14Benefits, Drawbacks, and Trade-offs

Every design has a price. An honest view helps you decide how much to rely on trace state.

Advantages

  • Safe extensibility. Vendors can add private notes without breaking the standard identity.
  • Interoperability. Tools from different companies can share one trace and still keep their own extras.
  • Optional. A trace works perfectly well with an empty trace state, so the cost is zero when unused.
  • Immutable. Copies cannot be changed by accident, which avoids hard-to-find bugs.
  • Carries sampling facts. Probability notes let backends estimate real traffic correctly.
  • Standard rules. Clear limits on size, characters, and order keep sharing predictable.

Disadvantages

  • Paid on every call. Every byte is multiplied by the number of network calls.
  • Silent failures. Lost or damaged notes do not cause errors, only quiet degradation.
  • All-or-nothing parsing. One invalid entry can cause the receiver to discard the whole list.
  • Crosses trust boundaries. Notes travel to partners and third parties unless you stop them.
  • Opaque. Values are meaningful only to their owner, so debugging requires knowing each vendor’s format.
  • Easy to misuse. It is tempting to treat it as free storage for application data.

Common trade-offs in plain language

ChoiceWhat you gainWhat it costs
Let vendors write notesRicher vendor features and better correlationLarger headers and more things to monitor
Allow no notes at allSmallest headers and no leakageSome vendor features and sampling statistics may stop working
Keep notes from partnersSmooth cooperation across companiesExposure to crafted or oversized input
Clear notes at the edgeStrong protection of your insidePartners cannot continue their own notes into your system
Carry sampling probabilityCorrect estimates of real trafficA few extra bytes per call, and discipline about who writes it
Store a fact as an attribute insteadNo per-call costOther services cannot see it during the request
Store a fact in baggage insteadApplication code can read itLarger headers and a bigger privacy surface
i
A Fair Rule of Thumb

Most programs need to do nothing with trace state except forward it. Add or change entries only when a specific tool or feature needs it, and keep the total small enough that you could write it on the back of an envelope.

When trace state is the wrong tool

If you find yourself wanting to carry customer details, feature flags, or business decisions from service to service, trace state is not the right place. Those are application facts, and they belong in baggage or in explicit request parameters. If you want to record details only for people to read later, use span attributes. Trace state is reserved for the narrow job of letting tracing tools share notes about the trace itself.

Counting the Real Benefit

When deciding whether to use vendor notes, count the benefit as carefully as the cost. A note that lets a vendor link a trace to its own record may save engineers ten minutes per investigation, and a team that investigates twenty incidents a month saves more than three hours. A note that lets a backend scale sampled counts correctly may prevent a wrong capacity decision worth far more than any header cost. Write the benefit down next to the byte cost, and let the comparison guide the decision, not habit or enthusiasm.

15Good Habits and Design Patterns

Patterns are tried-and-true recipes. Habits are the small routines that keep trace state small, safe, and useful.

Pattern

Pass through by default

Every service, proxy, and gateway forwards both trace headers unchanged unless it has a specific reason to touch its own entry.

Pattern

One entry per tool

Each tool owns exactly one key and updates it in place, instead of appending new entries at each hop.

Pattern

A size budget

Agree on a company-wide limit, such as a few hundred bytes, and alert when any trace state exceeds it.

Pattern

An edge checkpoint

At the public entrance, enforce size and format rules, and clear notes from callers you do not trust.

Pattern

Strip on egress

When calling outside companies, remove or limit trace headers, so internal notes never leave your trust boundary.

Pattern

Opaque codes, never facts

Store short meaningless references, not names, addresses, or descriptions, so a leaked header reveals nothing.

Habits that pay off

  • Know which tools write notes. Keep a short list of every tool and the key it owns.
  • Use standard libraries. Well-tested propagators handle the format rules correctly, including ordering and limits.
  • Never hand-build the header. Manual string editing is how invalid entries are born.
  • Keep values short. A value of a few dozen characters is plenty for almost every use.
  • Test the whole path. Send a test request end to end and check the notes at the far end.
  • Measure the size. Track the typical and maximum size of the header, and watch for growth after releases.
  • Document your keys. If your own team uses a key, write down what it means, who owns it, and when it can be removed.
  • Remove what you no longer use. Retire notes when the tool or feature that needed them is gone.

A gentle path to taking control

1

Look at what is there

Capture real trace state from several requests, and list every key, its owner, and its size.

2

Confirm forwarding

Check that every hop, including proxies and queues, forwards both headers.

3

Set a budget

Decide on a maximum size and raise an alert when it is passed.

4

Guard the boundaries

Add the edge checkpoint for incoming traffic and stripping for outgoing calls to outside parties.

5

Review regularly

Revisit the list each quarter, and retire notes nobody needs.

i
Make the Easy Path the Right Path

If the standard propagator is the default in every shared template, every new service forwards trace state correctly without anyone thinking about it. Make correct behaviour the effortless one.

Keeping Documentation Close to the Code

A pattern that works well is to keep a short document describing every trace state key in the same repository that configures the propagators. The document names the key, its owner, the meaning of its value in plain words, its expected size, and the date it should be reviewed. Because the document lives beside the configuration, it is updated in the same change, and reviewers notice when a new key appears without an explanation. This small habit prevents the slow accumulation of mysterious entries that nobody dares to remove.

16Common Mistakes, Anti-patterns, and Myths

Most trace state problems come from the same few mistakes. Knowing them in advance saves a lot of confusion.

AP-1The Hidden Data PipeVery common

What happens: A team uses trace state to carry business facts, such as a customer tier or a feature flag, from service to service.

Why it hurts: Trace state is for tracing tools, it is limited in size, and it crosses boundaries to partners. Business logic that depends on it breaks when a hop drops the header.

Better approach: Use baggage or explicit request parameters for application facts, and keep trace state for tracing tools.

AP-2The Ever-Growing ListCostly

What happens: A tool appends a new entry at every hop instead of updating its own single entry.

Why it hurts: The header grows until it crosses limits, requests are rejected, and the cost per call rises.

Better approach: One key per tool, updated in place, with an agreed maximum size.

AP-3The Hand-Built HeaderQuietly harmful

What happens: Someone writes the header by joining strings manually, with a capital letter in a key or a comma inside a value.

Why it hurts: The receiver finds the format invalid and discards the whole list, erasing every vendor’s notes without any error message.

Better approach: Always use the library operations to add, update, and delete entries.

AP-4The Trusting Front DoorRisky

What happens: The public entrance accepts whatever trace state arrives from the internet, and passes it deep inside.

Why it hurts: Outsiders can send oversized or crafted notes that waste resources or trick vendor tools.

Better approach: Enforce limits and clear untrusted notes at the edge.

Myths worth clearing up

MythReality
“Trace state is required for tracing to work.”It is optional. A trace works perfectly with an empty trace state.
“Trace state and baggage are the same.”Trace state is for tracing tools. Baggage is for application facts. They use different headers.
“I can edit trace state in place.”It is immutable. Every change creates a new copy.
“The sampled flag lives in trace state.”The flag is in the main traceparent header. Trace state may carry extra sampling details.
“Trace state is secure.”It travels in plain text and can be set by anyone who can send a request.
“Bigger is better because it carries more context.”Every byte is paid on every call. Small and opaque is best.
“A service that does not use vendor tools can ignore the header.”It must still forward it, or downstream vendors lose their notes.

A Review Checklist Before You Add a Key

  • Is it really needed? Could an attribute or baggage item do the job instead?
  • Is the value small and opaque? It should be a short code, not a description or a name.
  • Does it follow the format? Lowercase key, no commas or equals signs in the value, within the length limits.
  • Is there one entry per tool? Updated in place, never appended at each hop.
  • Is the boundary guarded? Incoming notes checked at the edge, outgoing notes stripped for outsiders.
  • Has the whole path been tested? One request, end to end, with the notes checked at the far end.

Five minutes with this list before each change prevents nearly all of the problems described above.

17A Worked Example: The Riverside Books Shop

Let us follow one imaginary online bookshop from a tangle of headers to a tidy, safe setup, using simple numbers.

Riverside Books has a gateway, a checkout service, and a payment service, and it calls an outside delivery partner. Two vendor tools are in use. A performance monitoring tool, whose key we will call rojo, writes a short note at the gateway. A routing tool, whose key we will call congo, writes a note at the checkout service. The shop handles about 10,000,000 internal calls per day.

Step 1: see what actually travels

The team captures the trace state at each hop of a test request.

HopTrace state seenWhat happened
Customer to gatewayEmptyNo notes yet; the customer’s browser sent none
Gateway to checkoutrojo=00f067aa0ba902b7The monitoring tool adds its note
Checkout to paymentcongo=t61rcWkgMzE,rojo=00f067aa0ba902b7The routing tool adds a note at the left
Payment to delivery partnercongo=t61rcWkgMzE,rojo=00f067aa0ba902b7Both internal notes leave the company

The last row alarms the team. The delivery partner, an outside company, receives internal notes that are meaningless to it and might reveal something about the shop’s tools.

Step 2: count the cost

The two notes together are about 60 bytes. Over 10,000,000 calls a day, that is 60 × 10,000,000 = 600,000,000 bytes, about 0.6 gigabytes per day. That is acceptable. But the team also discovers that a misconfigured tool adds an 80-byte entry at each of six hops, appending a new one each time instead of updating its single entry. That adds 6 × 80 = 480 bytes, and the total grows to 540 bytes per call, or 540 × 10,000,000 = 5,400,000,000 bytes, about 5.4 gigabytes per day. The trace state has become nine times larger for no benefit.

0.6 GB
Daily traffic from two tidy notes (60 bytes × 10,000,000 calls)
5.4 GB
Daily traffic after the runaway appending tool (540 bytes × 10,000,000)
9×
Growth caused by one misconfigured tool

Step 3: fix the growth

The team configures the misbehaving tool to update its own single entry in place. The header returns to about 60 bytes, and a new alert fires whenever any trace state exceeds 200 bytes. The savings are immediate: from 5.4 gigabytes per day back to 0.6.

Step 4: guard the boundaries

At the public gateway, the team adds a checkpoint: headers above a size limit are rejected, malformed trace state is dropped, and notes from callers that are not known partners are cleared. At the payment service’s outgoing call to the delivery partner, the team strips the trace headers, because the partner has no use for the shop’s internal notes. The partner still receives a fresh trace identity if both sides agree to cooperate, but no private notes.

Step 5: verify

The team sends a test request through the complete path and checks the trace state at every hop. The notes appear in the right order, the size is about 60 bytes, and the delivery partner receives nothing it should not. A hostile test with an oversized header from outside is rejected at the gateway. After a week of production traffic, the size alert has never fired, and vendor features that depended on the notes are working normally.

Everyday Analogy

The team behaved like a mail room supervisor. First she looked at what was stuck on the parcels, then removed the sticker that someone was adding at every desk, then put a clerk at the front door to check incoming parcels and another at the outgoing door to peel off private stickers before parcels left the building.

i
What This Example Teaches

The same standard header can cost 0.6 gigabytes or 5.4 gigabytes per day, and can either protect or expose your internals. The difference is not the feature but the care: one entry per tool, a size budget, and guards at the doors.

18Real-World Scenarios

Three short stories show how trace state behaves in everyday situations.

Scenario 1: The feature that worked only sometimes

A team notices that a vendor’s correlation feature links traces to its own records for most requests, but not for requests that pass through a particular queue. Comparing trace state on spans before and after the queue shows that the vendor’s entry disappears at that point. The queue client does not copy the tracestate header into message properties. After the team fixes the client, the feature works for every request. The lesson: compare the notes before and after each hop.

Scenario 2: The sudden wave of rejected requests

After a release, a small percentage of requests are rejected by a proxy with a header-too-large error. Engineers measure the trace state and find that it grows with every hop, because a newly upgraded tool appends an entry each time. Rolling back the tool stops the errors, and a corrected configuration updates a single entry instead. The lesson: unbounded growth is the most common way trace state hurts.

Scenario 3: The skewed traffic estimate

A dashboard that estimates real request counts from sampled traces shows a number twice as high as the load balancer reports. Investigation shows that one service overwrote the sampling note with its own value, so the backend scaled the counts incorrectly. Once only the sampler is allowed to write that entry, and every other tool forwards it unchanged, the estimates match reality. The lesson: protect the notes that other machinery depends on.

i
The Pattern Behind the Stories

In every story, the cure was to restore discipline: forward everything, update only your own entry, and keep it small. Trace state works beautifully when every party plays by the same few simple rules.

Choosing Where to Begin

If your organisation is only starting out, do not try to master everything at once. Begin by capturing real trace state from a few requests and writing down what you see. Confirm that every hop forwards both headers. Set a size budget and an alert. Then add the edge checkpoint and the outgoing strip for outside parties. Each step is small, brings a clear benefit, and can be tested alone. Trace state is a humble feature, but handled with care it gives every tool a safe place for its notes, and gives every engineer a clear clue when something goes wrong.

19Frequently Asked Questions

Q1What is trace state in one sentence?
Trace state is an optional, ordered list of short key and value notes, carried in the span context and in the tracestate header, that lets tracing tools share vendor-specific information along a trace without disturbing the standard identity.
Q2Is trace state required?
No. A trace works perfectly with an empty trace state. It is used only when a tool needs to attach extra information.
Q3What is the difference between traceparent and tracestate?
Traceparent carries the essential identity: version, trace identity, parent span identity, and flags, including the sampled flag. Tracestate carries optional vendor notes. Tracestate is ignored if traceparent is missing or invalid.
Q4What is the difference between trace state and baggage?
Trace state is for tracing tools and is stored in the span context. Baggage is for application code and is carried in its own header. Choose trace state for tracing machinery and baggage for business facts.
Q5How many entries can trace state hold?
At most 32 entries under the W3C standard, with each key and value limited to 256 characters. In practice, keep the whole list to a few hundred bytes at most.
Q6Why is the newest entry placed first?
So that readers can see which vendor modified the trace most recently, and so that if the list ever has to be shortened, the entries at the right, which have been unchanged the longest, are removed first.
Q7Why is trace state immutable?
So that every holder of a copy sees a stable value. Changes produce a new copy, which prevents accidental edits from affecting other parts of a program that run at the same time.
Q8What happens if the header is invalid?
The standard says the receiver should discard the whole trace state and start with an empty one. The trace itself continues, because identity is carried separately in traceparent.
Q9Should my service forward trace state it does not understand?
Yes. Forwarding unknown entries unchanged is essential, so that vendors elsewhere in the chain do not lose their notes.
Q10Can I put customer data in trace state?
No. It travels in plain text across every call, possibly to other companies. Use only short, opaque codes, and use baggage or explicit parameters for application facts, with careful thought about privacy.
Q11How does trace state relate to sampling?
The sampler receives the incoming trace state and returns a trace state with its decision. Newer consistent probability sampling uses a reserved entry to carry the sampling probability so backends can estimate real traffic accurately.
Q12Does the exported span include trace state?
Yes. The OTLP format has a field for the span’s trace state, and links carry their own, so backends can read the notes.
Q13Should I strip trace state at the edge?
Often yes for untrusted callers. Enforce size and format limits, and clear notes from callers you do not trust. For outgoing calls to outside companies, strip or limit the headers as well.
Q14Do the details differ between languages?
The idea and the standard format are the same. The names of the operations and where they are exposed can differ slightly between SDKs, so check your language’s current documentation before relying on a specific detail.

20Summary and Key Takeaways

Trace state is a small idea with a quiet but important role: a safe place for tools to leave notes.

We began with a parcel that carries a standard label and a few private stickers, and we ended with a clear picture of trace state. We saw where it sits in the span context, how it appears in the tracestate header, and which rules keep it safe to share. We followed it from service to service, watched how it is added, updated, and deleted, and saw how samplers use it to carry probability. We compared it with baggage and attributes, counted its cost, guarded its boundaries, and learned to check it. The cure for almost every trace state problem is the same: forward everything, change only your own entry, and keep it small.

Key Takeaways

  • Trace state is a list of vendor notes. It is the optional, open-ended part of the span context, carried in the tracestate header next to traceparent.
  • It is optional and ignorable. A trace works perfectly with an empty trace state, and it is ignored if traceparent is invalid.
  • Follow the format rules. At most 32 entries, short lowercase keys, values without commas or equals signs, newest entry first. One invalid entry can erase the whole list.
  • It is immutable. Add, update, and delete create new copies. Change only your own entry, and forward all others unchanged.
  • Sampling uses it. Samplers can read and extend the trace state, and consistent probability sampling uses a reserved entry so backends can estimate real traffic.
  • Keep it small and harmless. Every byte is paid on every call, so aim for a few hundred bytes, and never place secrets or personal data in it.
  • Guard the boundaries and check the path. Clear untrusted notes at the edge, strip headers on calls to outsiders, and test end to end after every change.