Skip to content

Agents entered via onRequest never get gen_ai.conversation.id #22845

Description

@sergical

How do you use Sentry?

Sentry SaaS (sentry.io)

Which SDK are you using?

@sentry/cloudflare

SDK Version

10.69.0

Framework Version

agents 0.20.0, ai 7.0.37, workers-ai-provider 4.0.0

Reproduction Example/SDK Setup

An Agent that handles HTTP requests and makes a model call. No manual Sentry code anywhere:

// src/server.ts
export const OrderSupportAgent = Sentry.instrumentAgentWithSentry(sentryOptions, OrderSupportAgentBase);

export default Sentry.withSentry(sentryOptions, {
  async fetch(request, env) {
    return (await routeAgentRequest(request, env)) ?? new Response('Not found', { status: 404 });
  },
});

// src/agents.ts
export class OrderSupportAgentBase extends Agent<Env> {
  async onRequest(request: Request): Promise<Response> {
    const body = await request.json();
    return this.runTurn(body); // calls generateText() -> Workers AI
  }
}

Steps to Reproduce

  1. Deploy the Worker above (@sentry/cloudflare 10.69.0, agents 0.20.0).
  2. POST /agents/order-support-agent/<instance> — the agents router sends this to onRequest.
  3. Open the resulting trace in Sentry and look at gen_ai.conversation.id on the spans.

Expected Result

The gen_ai spans carry gen_ai.conversation.id set to the agent instance name, the same way
they do for a chat turn or a @callable() RPC call. That is what the Conversations view groups on.

Actual Result

gen_ai.conversation.id is empty on every span. In my trace: 14 spans (http.server ×3,
gen_ai.invoke_agent ×2, gen_ai.generate_content ×5, gen_ai.execute_tool ×3, rpc ×1), none
of them stamped.

Adding a manual Sentry.setConversationId() inside the same agent stamps all of them, so
everything downstream works — the SDK just never sets the id on this path.

Additional Context

setAgentConversationId (packages/cloudflare/src/instrumentations/agents/types.ts) has exactly
two callers:

  • instrumentChatAgentConversation.ts — the onChatMessage proxy
  • instrumentAgentCallableRpc.ts — the onMessage proxy (@callable() over WebSocket)

An HTTP request reaches the agent through the obj.fetch proxy in durableobject.ts, which wraps
the handler in wrapRequestHandler and never calls it. instance.name — the value it would use —
is available on this path too.

onRequest isn't an edge case: the agents docs list it as one of the two ways into an agent
("HTTP requests via onRequest() — REST APIs, webhooks, file uploads"), and a webhook can't hold
a WebSocket.

Worth noting that dev-packages/e2e-tests/test-applications/cloudflare-agent/tests/ai-streaming.test.ts
already drives both fixture agents over HTTP and asserts a bunch of gen_ai.* attributes — it just
never asserts gen_ai.conversation.id, which is why CI is green today.

I have a fix and will open a PR against this issue.

Metadata

Metadata

Assignees

No one assigned

    Projects

    Status
    Waiting for: Product Owner

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions