Skip to content

[BUG] Claude harness: Claude Code exports no spans against the real Anthropic API #2922

Closed

Description

@djannot

Disclosure: this issue was drafted by an AI agent (Claude) while testing kagent 1.x on Agent
Substrate, and reviewed by a human before filing.

Severity: Medium · tracing enabled, Claude harness with the real Anthropic API · no workaround found

What happens

As a user I turn on tracing and send a turn to an agent on the claude Harness. The trace holds only
the harness runtime's spans (invoke_agent and the A2A and gRPC spans around it). There is no
claude_code.* span, so the model calls and tool calls inside Claude Code are not traced (2 of 2
runs). The same setup on the kagent runtime has generate_content spans, and on the Codex harness
run_sampling_request spans.

The egress gateway log for the Claude actor during the turn shows that Claude Code never calls the
OTLP endpoint. Both Export calls come from the harness runtime's Go exporter:

2  api.anthropic.com                  /v1/messages?beta=true                                        200
2  api.anthropic.com                  /api/event_logging/v2/batch                                   200
1  api.anthropic.com                  /api/eval/sdk-…                                               200
1  api.anthropic.com                  /api/claude_code/metrics                                      200
2  jaeger.jaeger.svc.cluster.local    /opentelemetry.proto.collector.trace.v1.TraceService/Export   200
   user-agent of both Export calls: "OTel OTLP Exporter Go/1.45.0 grpc-go/1.83.2"

The collector is reachable from the actor, and Claude Code does send its own telemetry to
api.anthropic.com, but it sends no span to the configured collector.

Reproduce

kagent v1.0.0-alpha2 on Agent Substrate, the chart's otel.tracing pointed at an OTLP gRPC
collector (Jaeger http://jaeger.jaeger.svc.cluster.local:4317), a claude Harness, and an
AgentTemplate with an Anthropic ModelConfig (real API key). Send one turn, then list the span names
of its trace, and read
kubectl -n ate-system logs deploy/atenet-egress -c agentgateway | grep ai-<instance id>.

Expected vs actual

  • Expected: the trace holds claude_code.* spans for the model and tool calls, as the e2e test
    requires.
  • Actual: only the harness runtime's spans; Claude Code makes no OTLP export.

Ask

Find why Claude Code does not export against the real API, and make the e2e test cover that path.

What kagent configures, and why the e2e test passes

The Claude process gets (go/core/internal/translator/claude/compiler.go, lines 97-99, plus the
shared trace environment):

CLAUDE_CODE_ENABLE_TELEMETRY=1
CLAUDE_CODE_ENHANCED_TELEMETRY_BETA=1
OTEL_TRACES_EXPORTER=otlp
OTEL_EXPORTER_OTLP_TRACES_ENDPOINT=http://jaeger.jaeger.svc.cluster.local:4317
OTEL_EXPORTER_OTLP_TRACES_PROTOCOL=grpc
OTEL_METRICS_EXPORTER=none
OTEL_LOGS_EXPORTER=none
KAGENT_PRE_RESPONSE_TRACE_FLUSH=true

go/core/test/e2e/tracing_test.go fails on "no claude_code.* span was exported before suspension",
but it runs Claude Code against a mock model at a custom Anthropic base URL
(createClaudeMockModel). Against the real API, the spans do not appear.

Candidate causes (none verified)
  • Claude Code gates the enhanced-telemetry beta on a remote feature flag it fetches from
    api.anthropic.com/api/eval/sdk-…; with a custom base URL that lookup may not happen.
  • Claude Code reads only the generic OTEL_EXPORTER_OTLP_ENDPOINT / OTEL_EXPORTER_OTLP_PROTOCOL,
    not the per-signal …_TRACES_… variables.
  • Claude Code batches its spans, and the actor suspends before it flushes them;
    KAGENT_PRE_RESPONSE_TRACE_FLUSH covers only the harness runtime's exporter.
Scope
  • Reproduced on kind with kagent v1.0.0-alpha2, Agent Substrate 0.2.0-beta5, harness image
    ghcr.io/kagent-dev/kagent/claude-harness@sha256:6794f2d25be053766030dcd15aa0da58747a425bcf423e851ad0b6870d79d1b5
    (Claude Code 2.1.260). Code unchanged on main at c61fad8b (2026-09-23).
  • Unaffected: the harness runtime's own spans on Claude; tracing on the kagent and Codex runtimes.

Activity

  1. krisztianfekete commented on Sep 24, 2026

    @krisztianfekete
    Contributor

    Thanks for opening this issue!

    We have seen this working during adding support for this in #2805, and I also got this working locally before. Now I was able to reproduce it, but it was working in some cases, which makes me think if it could be a problem with the flushing mechanism.

    @supreme-gg-gg, can you please take a look at this when you have some free cycles?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    ai-generatedbackendissue affecting the autogen runtimebugSomething isn't working

    Type

    No type

    Projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions