Counted on 13 August 2026, the OpenTelemetry GenAI registry defines 63 gen_ai attribute keys and every single one is marked Development. None is stable, none is a release candidate, and the conventions document header says Development in its first line. Version 1.42.0 of the main semantic conventions, published 12 June 2026, deprecated the entire gen_ai namespace in the main repository and moved it to a dedicated GenAI repository that has not cut a tagged release yet, so there is no version number to pin against. The blank-dashboard failure most teams hit is not the repo move: it is the Python instrumentation package swap, where the deprecated package emitted v1.30.0-era attribute names unless OTEL_SEMCONV_STABILITY_OPT_IN was set, and the replacement emits the latest experimental conventions unconditionally with no opt-out. Build cost and latency dashboards on the four client metrics rather than span attributes, treat agent and tool panels as provisional because five breaking changes are already queued unreleased, and for regulated deployments use the external upload hook so message bodies stay on storage you control while only a reference rides the span.
Count them yourself. The OpenTelemetry GenAI registry defines 63 gen_ai attribute keys, and on 13 August 2026 every single one is marked Development. Not one is Stable. Not one is a release candidate. Including enum member declarations, the registry file holds 109 stability declarations and all 109 read development. The conventions document says it in its own first line of body text: Status, Development.
That matters because a lot of the guidance published on the OpenTelemetry GenAI semantic conventions asserts the opposite, and much of it prints an attribute list with no spec version attached. Some of those lists still show the pre-v1.37.0 model: content in per-message events, gen_ai.system as the current provider attribute, and a gen_ai.response.prompt_tokens that appears in no version of the registry, current or deprecated. If you paste one of those into a dashboard, it will render an empty panel and tell you nothing is wrong.
Everything below was checked against semantic-conventions v1.44.0 and the semantic-conventions-genai main branch on 13 August 2026, and the GenAI repository has not yet cut a tagged release. Attribute names and version numbers are load-bearing here, so the source for each claim is the spec file, not a summary of it. For the wider tooling context, this sits in our AI development tools pillar.
What actually changed, in release order
The churn is easier to reason about as a timeline than as a diff. Four events matter.
The rename most teams tripped over landed in 1.37.0: gen_ai.system became gen_ai.provider.name, and the per-message event model was replaced by three attributes carrying the whole conversation. That was a content model change, not a signal change. Content did not move to logs and it did not move to a separate signal. It moved to opt-in structured span attributes, with an optional detail event and an optional external upload hook alongside.
Version 1.42.0 is the one that breaks your monitoring of the spec itself. Published on 12 June 2026, it deprecated every gen_ai.* attribute, metric, event and span previously defined in the main repository. The two releases that followed contain no gen_ai entries whatsoever. If you track the core semantic conventions version to know when your LLM attributes change, you are now watching the wrong repository.
The new home, open-telemetry/semantic-conventions-genai, was created on 5 May 2026 and has zero tagged releases as of 13 August 2026. Its README still lists the schema URL as a to-do. There is no version number to pin against, which means the conventions you are running are whatever sat on the main branch the day your instrumentation library was built.
| Version | Published | What it did to gen_ai |
|---|---|---|
| v1.37.0 | 2025-08-25 | Renamed gen_ai.system to gen_ai.provider.name, replaced per-message chat events with gen_ai.system_instructions, gen_ai.input.messages and gen_ai.output.messages, dropped the gen_ai prefix from gen_ai.openai.* |
| v1.38.0 to v1.41.0 | late 2025 to spring 2026 | Agent and tool additions layered on top of the inference span |
| v1.42.0 | 2026-06-12 | Deprecated the entire gen_ai namespace in the main repository and moved it, plus the OpenAI and MCP models, to a dedicated GenAI repository |
| v1.43.0, v1.44.0 | 2026-07-03, 2026-08-04 | No gen_ai entries at all |
Stability, checked key by key
Development is not a soft warning. OpenTelemetry's own document status definition says a component at that level MAY be removed without prior notice and SHOULD NOT be used in production, with no backward-compatibility promise attached. For semantic conventions that has meant keys renamed, retyped and deleted without a major version bump. Everything in the GenAI namespace sits there.
The honest reading of that table: nothing is guaranteed, but the metric surface is far smaller and has moved far less than the span surface. Cost and latency panels built on gen_ai.client.token.usage split by gen_ai.token.type will outlive panels built on span attributes. If you are doing per-tenant LLM cost attribution, put the tenant dimension on the metric, not only on the span.
| Attribute group | Representative keys | Stability | Practical risk |
|---|---|---|---|
| Provider and operation | gen_ai.provider.name, gen_ai.operation.name | Development | Low. Required on the inference span, changed once, widely emitted |
| Request parameters | gen_ai.request.model, gen_ai.request.seed, gen_ai.request.top_k | Development | Medium. A top_k type change is queued unreleased |
| Usage and tokens | gen_ai.usage.input_tokens, gen_ai.usage.output_tokens, gen_ai.token.type | Development | Low. Renamed once in the deprecation pass, stable in practice since |
| Content | gen_ai.system_instructions, gen_ai.input.messages, gen_ai.output.messages, gen_ai.tool.definitions | Development, opt_in | High. Not emitted by default, and shape narrowing is queued |
| Agent and tool | gen_ai.agent.id, gen_ai.agent.version | Development | High. Multiple removals and scope changes queued unreleased |
| Client metrics | gen_ai.client.token.usage, gen_ai.client.operation.duration | Development | Lowest surface in the spec. Build cost dashboards here |
The deprecated attribute map
This is the table to keep. Old name on the left, what to query instead on the right.
Note the asymmetry. Eight of these are clean renames with a stated replacement. gen_ai.prompt and gen_ai.completion are marked obsoleted with the note that they were removed with no replacement at this time, because content did not get a successor key, it got a different capture model.
Five per-message events are gone too: gen_ai.system.message, gen_ai.user.message, gen_ai.assistant.message, gen_ai.tool.message and gen_ai.choice. If your log pipeline filters on those event names, it now filters on nothing.
| Deprecated | Replacement | If you do nothing |
|---|---|---|
gen_ai.system | gen_ai.provider.name | Provider breakdowns return zero rows |
gen_ai.usage.prompt_tokens | gen_ai.usage.input_tokens | Input token cost reads as 0 |
gen_ai.usage.completion_tokens | gen_ai.usage.output_tokens | Output token cost reads as 0 |
gen_ai.prompt | None. Obsoleted, "Removed, no replacement at this time" | Prompt panels silently empty |
gen_ai.completion | None. Obsoleted, same note | Completion panels silently empty |
gen_ai.openai.request.seed | gen_ai.request.seed | Reproducibility filters break |
gen_ai.openai.request.response_format | gen_ai.output.type | Structured-output filters break |
gen_ai.openai.request.service_tier | openai.request.service_tier | Key loses the gen_ai prefix entirely |
gen_ai.openai.response.service_tier | openai.response.service_tier | Same |
gen_ai.openai.response.system_fingerprint | openai.response.system_fingerprint | Same |
Why the dashboards go blank
The repo move is not what breaks production. The package default flip is.
On the Python side, the old instrumentation, opentelemetry-instrumentation-openai-v2, is now deprecated and receives security patches only. Its README states that without OTEL_SEMCONV_STABILITY_OPT_IN set to gen_ai_latest_experimental, it aligns with Semantic Conventions v1.30.0 and does not capture details introduced in later versions. Its replacement, opentelemetry-instrumentation-genai-openai 1.0b0, published 9 July 2026 from a separate repository that carries twelve GenAI instrumentation packages and leans on the shared opentelemetry-util-genai helper library maintained in the Python contrib repo, states that the latest experimental conventions are used unconditionally and there is no environment variable to opt in or out. The old package's own README warns that the replacement contains breaking changes relative to it.
So the upgrade path is: bump a package, and every attribute name on every LLM span repoints at once.
Nothing in that sequence produces an error. Spans are created, the exporter returns success, the collector reports healthy throughput, and the backend ingests normally. The only observable symptom is a query that matches zero spans, which is visually identical to a healthy system that happened to be quiet. That failure signature is the same one behind most of the cases in tracing AI failures in production: the telemetry is fine, the question you are asking it is stale.
One config value changed meaning across the same migration. In the deprecated package, OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT documented true as the legacy value that turned content capture on for the old per-message events, alongside span_only, event_only and span_and_event for anyone who had opted into the latest experimental conventions. The replacement documents only no_content (the default), span_only, event_only and span_and_event, and true is gone from the list. A manifest still carrying true is a line nobody reads until someone asks where the prompts went.
Instrumenting a chat call so it survives the next release
If you hand-roll one span, make it the inference client span. The contract is small and it is the most settled thing in the namespace.
span name: {gen_ai.operation.name} {gen_ai.request.model} e.g. "chat gpt-5"
span kind: CLIENT (MAY be INTERNAL for an in-process model)
required: gen_ai.operation.name e.g. chat
required: gen_ai.provider.name
conditionally req.: gen_ai.request.model (if available)
sampling relevant: gen_ai.operation.name, gen_ai.provider.name,
gen_ai.request.model, server.address, server.portThe sampling-relevant flag is the operational detail people miss. Those attributes have to exist at span creation, not be added before the span ends, or a tail sampler cannot see them and your sampling policy quietly stops discriminating. Allowed gen_ai.operation.name values include chat, generate_content, text_completion, embeddings, retrieval, fetch_response, create_agent, invoke_agent, execute_tool and invoke_workflow.
On metrics, four client instruments are defined: gen_ai.client.token.usage (histogram, unit {token}, with gen_ai.token.type required), gen_ai.client.operation.duration (histogram, seconds), plus gen_ai.client.operation.time_to_first_chunk and gen_ai.client.operation.time_per_output_chunk for streaming latency. Three server-side histograms cover the self-hosted serving path: gen_ai.server.request.duration, gen_ai.server.time_per_output_token and gen_ai.server.time_to_first_token. If you run your own inference tier, those three are what connect model serving to the quality, drift and cost monitoring layer above it.
Pin the fields you care about yourself rather than inheriting them. Deployment identity, tenant, model version as you deployed it, and your own routing decision belong on attributes you name and own, because those will never be renamed by a working group.
Agents, tools and MCP are the parts still moving
This is not finished breaking. Five breaking changes sat unreleased in the GenAI repository's changelog queue on 13 August 2026, and four of the five land on agent, tool or retrieval telemetry:
gen_ai.request.top_k changes type from double to int and is scoped to decoding only, with retrieval moving to a new gen_ai.retrieval.top_kgen_ai.agent.id scope clarified and removed from internal agent spansgen_ai.system_instructions part types limited to textgen_ai.provider.name removed as a required attribute from the invoke_agent internal spaninvoke_agent and execute_tool attributes aligned across spans and duration metrics, with gen_ai.agent.version removed from the invoke_agent internal spanThree of those remove an attribute from a span that currently carries it. The practical rule is blunt: build chat and cost dashboards on the inference span and the client metrics, and treat every agent, tool and retrieval panel as provisional until the GenAI repository cuts a tagged release. MCP telemetry moved in the same v1.42.0 sweep, which is worth noting if you are already working through the MCP stateless spec migration: both moves land in the same upgrade window and it is easy to attribute one outage to the other.
Capturing prompts without shipping them off your network
For regulated deployments this is the important part of the whole spec, and it is a data-boundary decision before it is an observability decision.
All four content attributes (gen_ai.system_instructions, gen_ai.input.messages, gen_ai.output.messages, gen_ai.tool.definitions) carry requirement_level: opt_in. The spec is explicit that instrumentations should not capture instructions, inputs or outputs by default but should provide an opt-in, and it names three usage patterns: do not record, record on span attributes, or store content externally and record references on the spans. The detail event that carries the full request, gen_ai.client.inference.operation.details, is itself declared opt-in, and the Python flag controlling it, OTEL_INSTRUMENTATION_GENAI_EMIT_EVENT, is derived from the capture mode when you leave it unset: false under no_content or span_only, true under event_only or span_and_event.
Use the third pattern. Recording message bodies as span attributes means every prompt and completion travels through your exporter into whatever backend holds your traces, which for most teams is a hosted product with its own retention and its own subprocessor list. That is the decision that turns a tracing rollout into a data transfer question, and it is the same trap covered in preventing data leakage in AI applications.
# Content stays on storage you control; only a reference rides the span OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT=span_only OTEL_INSTRUMENTATION_GENAI_COMPLETION_HOOK=upload OTEL_INSTRUMENTATION_GENAI_UPLOAD_BASE_PATH=/var/lib/genai-prompts OTEL_INSTRUMENTATION_GENAI_UPLOAD_FORMAT=jsonl OTEL_INSTRUMENTATION_GENAI_UPLOAD_MAX_QUEUE_SIZE=20
A local filesystem path works exactly as well as object storage, which is what makes this viable inside an air-gapped boundary. The spec further states the upload hook should operate independently of the content-capture opt-in flags and should be invoked regardless of the span sampling decision, so your prompt archive stays complete even when traces are sampled at 1%. For on-premise work this is the configuration that lets a security review approve LLM tracing at all: traces go to the observability tier, message bodies never do. It also reframes the observability platform choice, because once content stays local, the vendor question shrinks to trace storage and query.
A migration checklist that does not require a second outage
Four steps, in order.
1. Dual-emit before you cut over. Run a collector transform that copies old names to new ones so both dashboards work during the transition. This is a config change, not a redeploy of every service:
processors:
transform/genai_rename:
error_mode: ignore
trace_statements:
- context: span
statements:
- set(attributes["gen_ai.provider.name"], attributes["gen_ai.system"])
where attributes["gen_ai.system"] != nil
- set(attributes["gen_ai.usage.input_tokens"], attributes["gen_ai.usage.prompt_tokens"])
where attributes["gen_ai.usage.prompt_tokens"] != nil
- set(attributes["gen_ai.usage.output_tokens"], attributes["gen_ai.usage.completion_tokens"])
where attributes["gen_ai.usage.completion_tokens"] != nil2. Version-stamp every dashboard query. Put the spec version the panel assumes in the panel description. When a query returns zero, the first question is which version it was written against, and right now nobody can answer it.
3. Add a contract test that fails CI when an attribute moves. Run one real instrumented call against a stub server, capture the exported span, and assert on the exact key set. This is the only mechanism that turns a silent rename into a red build:
REQUIRED = {"gen_ai.operation.name", "gen_ai.provider.name", "gen_ai.request.model"}
def test_span_attribute_contract(exported_span):
keys = set(exported_span.attributes)
assert REQUIRED <= keys, f"missing: {REQUIRED - keys}"
assert "gen_ai.system" not in keys, "deprecated provider attribute is back"
assert not keys & {"gen_ai.prompt", "gen_ai.completion"}, "obsoleted content keys present"4. Alert on absence, not only on value. A rule that fires when gen_ai.client.token.usage reports no data for 15 minutes catches the blank-dashboard failure in a quarter hour instead of at the next invoice. Every one of these outages is a query returning zero, and zero is a number your alerting has to treat as suspicious rather than calm.
The summary judgement: emit the conventions, because a shared vocabulary beats a bespoke one even while it moves. Do not build load-bearing infrastructure on the assumption that any gen_ai key is stable, because on 13 August 2026 none of the 63 is. Put cost on metrics, put content on your own storage, and keep the rename layer in the collector where it costs a config edit rather than a deploy.
FAQ
Quick answers to the questions this post tends to raise.



