> ## Documentation Index
> Fetch the complete documentation index at: https://braintrust.dev/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Root span id encoding for trace grouping

export const plans_0 = "Any"

export const deployments_0 = "Any"

export const data_plane_version_0 = undefined

export const use_case_0 = "Use case - Prevent trace-tree splits by standardizing root_span_id string encoding across emitters and provide emitter-side conversion examples and diagnostics"

<Note>
  **Applies to:**

  * Plan - {plans_0}
  * Deployment - {deployments_0}
  * {data_plane_version_0}
  * {use_case_0}
</Note>

## Summary

Symptom: a single 16-byte trace ID appears as two trace trees in the UI.\
Cause: two emitters send the same bytes encoded as different strings (hex vs dashed UUID). Braintrust groups traces by exact `root_span_id` string equality.\
Recommended fix: canonicalize `root_span_id` at the emitter(s) so all components send the same string form.

## What is happening

Braintrust treats `root_span_id` as an opaque string. There is no server-side byte-level canonicalization.\
If one emitter sends `00112233445566778899aabbccddeeff` and another sends `00112233-4455-6677-8899-aabbccddeeff` the platform sees two different `root_span_id` values.\
Both values represent the same 16 bytes, but they are different strings, so traces split into separate trees.

## Fix or suggestion

### Option 1: canonicalize at the emitter (recommended)

Pick one canonical string form (common choices below) and enforce it in every emitter or plugin.

* Choice A — dashed UUID (example): `00112233-4455-6677-8899-aabbccddeeff`
* Choice B — 32-char hex (no dashes, common OTel span format): `00112233445566778899aabbccddeeff`

Actionable steps:

1. Choose the canonical form for your service boundary.
2. Update each emitter/plugin to convert incoming root/span id bytes or strings to that form before emitting.
3. Add a unit test or runtime assertion to fail if an emitter would emit a non-canonical form.
4. Roll out changes and monitor for split traces.

Minimal conversion examples

Python (bytes -> dashed UUID or no-dash hex)

```python theme={"theme":{"light":"github-light","dark":"github-dark-dimmed"}}
import uuid

raw = b'\x00\x11\x22\x33\x44\x55\x66\x77\x88\x99\xaa\xbb\xcc\xdd\xee\xff'
dashed = str(uuid.UUID(bytes=raw))   # "00112233-4455-6677-8899-aabbccddeeff"
nodash = uuid.UUID(bytes=raw).hex    # "00112233445566778899aabbccddeeff"
```

Bash (32-hex \<-> dashed)

```bash theme={"theme":{"light":"github-light","dark":"github-dark-dimmed"}}
hex="00112233445566778899aabbccddeeff"
dashed=$(echo "$hex" | sed -E 's/^(.{8})(.{4})(.{4})(.{4})(.*)$/\1-\2-\3-\4-\5/')
# dashed -> hex
echo "$dashed" | tr -d '-'
```

### Option 2: normalize in a shared plugin or preprocessor

If one component is widely reused, normalize there so callers do not need changes.

Actionable steps:

1. Update the shared plugin to accept incoming bytes/hex and emit the chosen canonical string.
2. Add backward-compatible parsing for both forms (strip dashes or parse UUID) when reading inbound ids.
3. Deploy the plugin carefully, and run tests across services that use it to check for compatibility/regressions.

## How to confirm it worked

* Log-based check: for a known trace\_id, confirm there is exactly one root\_span\_id string.
  Example:
  ```bash theme={"theme":{"light":"github-light","dark":"github-dark-dimmed"}}
  jq -r 'select(.trace_id=="<TRACE_ID>") | .root_span_id' traces.json | sort | uniq -c
  ```
  Expect a single unique value.
* UI check: the trace with that trace\_id appears as one tree (no duplicate root spans).

## Notes

* Braintrust groups traces by exact root\_span\_id string equality; choose and enforce one encoding across emitters.
* Common practice: use the format already dominant in your environment (OTel default is 32-char hex for span IDs; some tools prefer dashed UUIDs).
