Feature Request: Propagate TRACEPARENT to subprocess environments for trace hierarchy
Summary
When Claude Code executes tools (especially Bash), the child process does not receive the OpenTelemetry trace context (TRACEPARENT environment variable). This breaks distributed tracing for applications that spawn subprocesses from Claude Code tool execution.
Problem
When building observability around Claude Code, we want to see the full trace hierarchy:
claude.execute
└── exec.turn
└── exec.tool_use (Bash: ailang check ...)
└── ailang.check (child process) ← NOT LINKED
└── compile.parse
└── compile.typecheck
Currently, child spans created by subprocesses end up in completely different traces because TRACEPARENT is not propagated:
Trace 1: claude.execute → exec.turn → exec.tool_use
Trace 2: ailang.check → compile.parse (orphaned, different trace_id!)
Proposed Solution
When Claude Code spawns a subprocess for tool execution (especially Bash), propagate the current trace context via environment variables:
# Standard W3C Trace Context format
TRACEPARENT=00-{trace_id}-{span_id}-01
This is the standard approach used by:
- OpenTelemetry auto-instrumentation
- Jaeger, Zipkin, and other tracing systems
- Most CI/CD systems (GitHub Actions, GitLab, etc.)
Use Case
We're building AILANG, a language for AI code synthesis. Our observability system (Observatory) tracks:
- Coordinator tasks that delegate to Claude Code
- Claude Code execution (turns, tool uses)
- Child processes spawned by Claude (ailang check, ailang run, etc.)
Without TRACEPARENT propagation, we have to use timestamp-based correlation as a workaround, which is fragile and only works when all spans are in the same trace.
Workaround (Current)
We currently work around this by:
- Using
task_idattributes instead of trace_id for linking - Timestamp-based correlation for spans within the same trace
- Script-based agents that explicitly set TRACEPARENT before calling
ailang exec
These workarounds add complexity and don't provide true distributed tracing.
Implementation Notes
The fix would be minimal - when spawning a subprocess, include the current span's trace context in the environment:
// Pseudocode
env := os.Environ()
if span := trace.SpanFromContext(ctx); span.SpanContext().IsValid() {
traceparent := fmt.Sprintf("00-%s-%s-01",
span.SpanContext().TraceID().String(),
span.SpanContext().SpanID().String())
env = append(env, "TRACEPARENT="+traceparent)
}
cmd.Env = env
Environment
- Claude Code version: Latest
- OS: macOS, Linux
- Using OTEL exporter to local collector
Related
This would enable proper integration with any OTEL-compatible tracing system and improve the developer experience for anyone building observability around Claude Code.
---
🤖 Generated with Claude Code
This issue has 3 comments on GitHub. Read the full discussion on GitHub ↗