FINDING 1
Content opt-outs do not cover exceptions
All three privacy settings we tested kept prompts, completions and tool arguments out of telemetry: TRACELOOP_TRACE_CONTENT=false, OPENINFERENCE_HIDE_INPUTS/_OUTPUTS, and openai-v2 NO_CONTENT. None of them stopped a secret inside an error message. Instrumentations copy the message into exception.message, exception.stacktrace and the span's status.message; openai-v2 sets status.message only.
- When it bites: a gateway or proxy echoes a rejected key, an HTTP error includes a URL with a key in it, or a validation error quotes user input. The OpenAI API itself masks keys in its 401 messages.
- Mitigation: none of these settings help. Scrub exception events and status in a span processor or exporter, for example traceleakcheck's
scrub().
FINDING 2
URL query secrets are exported by the httpx instrumentation
Reported: contrib#5279
opentelemetry-util-http 0.66b2 redacts only AWSAccessKeyId, Signature, sig and X-Goog-Signature. The semantic conventions also list X-Amz-Signature, X-Amz-Credential and X-Amz-Security-Token, so an S3 presigned URL is exported with its signature and STS session token. Neither list includes key, so APIs that take ?key= get it written into url.full.
FINDING 3
Logfire's default scrubbing skips LLM messages and exception messages
Logfire scrubs by pattern, and api[._ -]?key is one of its patterns. Even so, the prompt OPENAI_API_KEY=<secret> and the error Invalid API key: <secret> both left the process unscrubbed. This is deliberate in 5.4.0: gen_ai.input.messages and gen_ai.output.messages are on its SAFE_KEYS list, and a source comment says exception-message scrubbing was removed.
FINDING 4
openai-v2's documented legacy =true captures nothing
Docs fixed in the successor package
The openai-v2 2.4b0 README documents CAPTURE_MESSAGE_CONTENT=true as the legacy value. On opentelemetry-util-genai 1.x it is rejected, capture falls back to NO_CONTENT, and the only sign is a log warning. A team that thinks it's auditing captured content is auditing nothing. It nearly fooled this survey too, so the runner now flags any capture-enabled config that comes back clean as MISCONFIGURED.
openai-v2 now lives on as opentelemetry-instrumentation-genai-openai, whose README lists only the valid values.
FINDING 5
A fresh install of openai-v2 fails to import
Known: genai#997
pip install opentelemetry-instrumentation-openai-v2 resolves opentelemetry-util-genai 1.3b0, which no longer ships opentelemetry.util.genai.instruments, so the import fails with ModuleNotFoundError. Pin opentelemetry-util-genai==1.1b0, or move to opentelemetry-instrumentation-genai-openai.
FINDING 6
Streaming: split secrets are reassembled, and an abandoned stream can hide a leak
Known: genai#491
- Split across chunks: every instrumentation that records content reassembles the reply, so a secret split over two chunks is exported whole.
- Abandoned after one chunk: OpenLLMetry and OpenInference end the span and export the partial reply, secret included. Logfire ends it without the partial reply. openai-v2 never ends the span, and a span that never ends is never exported.
Our first streaming run reported that openai-v2 case as a false "clean". traceleakcheck now tracks spans that start during a test and never end, and reports the test as incomplete.