Limits
Things this genuinely can’t do. Worth knowing before you rely on it.
Live runs
A span only exports when it ends. So a run that’s still going, or one that hung, isn’t there at all, and it looks exactly like a run that never happened.
“Which run is stuck right now” is not a question you can answer this way. If you need that, close
long-running work as abandoned on a timer in your own server, or emit a log line at start.
This is a real step back from plain logging, which writes things as they happen.
Status
Span status has three values and instrumentation shouldn’t set ok, so in practice you get two.
completed and abandoned both look unset. failed and terminated both look like errors.
Every default dashboard reads that field. Yours needs to read
code_mode.execution.disposition instead, which is what the dashboard does.
Span names
If you don’t attest crossing.target, the call span’s name is whatever the program said it called.
Span-metrics tools, service maps and name-keyed alerts all key on span names, and none of them read
provenance.
Collector blocks the metrics mocon defines from doing this. It can’t stop a connector somebody else configured.
Unknown duration
A span always has a start and an end, so it always has a duration. A call you know settled but can’t time becomes a zero-duration span rendering as a tick. There’s an attribute saying so, and no trace viewer reads it.
Attestation
Nothing in a trace can tell apart a server genuinely watching its call boundary from one copying a value out of the program’s return and attesting it anyway. Catching that needs a second observer in the path under its own identity. No format does it.
Fixed sets
You can add a field next to a disposition. You can’t add a value to it, and a reader following the rules treats the fixed value as the real answer.
The case that bites: a run that pauses at the end of one dispatch and picks up in a later one. Each
dispatch is its own run, so the paused one reports completed, and one logical run looks like three
completed ones.
Outside
Sampling can drop part of a trace, so a missing call might mean sampled rather than never happened. Use a parent-based sampler.
Attribute length limits in the SDK cut values after mocon has already recorded what it did, so something captured whole can arrive shortened with nothing saying so. Set your own cap lower.
Runtime
The TypeScript package needs Node 20 or later. It uses Buffer, node:crypto and node:util, so it
won’t run on Workers, Deno, or in a browser. If your sandbox lives on one of those, that package
isn’t an option today.
The attributes themselves don’t care. They’re plain OpenTelemetry, the reference lists every one, and the specification says exactly what each means. Emitting them yourself from whatever runtime you’re on gets you the same spans, and a second implementation already does exactly that.
Not standard
code_mode.* is this project’s own namespace and nobody else has agreed to it. The gen_ai.* and
mcp.* attributes it reuses are still in development upstream with no compatibility promise, and
gen_ai.operation.name = execute_code isn’t an upstream value, so anything filtering on known
operation names won’t see these runs at all.