@@ -21,11 +21,7 @@ import { generateWorkspaceContext } from '@/lib/copilot/chat/workspace-context'
2121import { COPILOT_REQUEST_MODES } from '@/lib/copilot/constants'
2222import { TraceAttr } from '@/lib/copilot/generated/trace-attributes-v1'
2323import { TraceSpan } from '@/lib/copilot/generated/trace-spans-v1'
24- import {
25- createBadRequestResponse ,
26- createRequestTracker ,
27- createUnauthorizedResponse ,
28- } from '@/lib/copilot/request/http'
24+ import { createBadRequestResponse , createUnauthorizedResponse } from '@/lib/copilot/request/http'
2925import { createSSEStream , SSE_RESPONSE_HEADERS } from '@/lib/copilot/request/lifecycle/start'
3026import { startCopilotOtelRoot , withCopilotSpan } from '@/lib/copilot/request/otel'
3127import {
@@ -609,17 +605,27 @@ async function resolveBranch(params: {
609605}
610606
611607export async function handleUnifiedChatPost ( req : NextRequest ) {
612- const tracker = createRequestTracker ( false )
613608 let actualChatId : string | undefined
614609 let userMessageId = ''
615610 let chatStreamLockAcquired = false
616- // Started once we know the streamId (= userMessageId). Every subsequent
617- // span (persistUserMessage, createRunSegment, the whole SSE stream, etc.)
618- // nests under this root via AsyncLocalStorage / explicit propagation,
619- // and the stream's terminal code path calls finish() when the request
620- // actually ends. Errors thrown from the handler before the stream
621- // starts are finished here in the catch below.
611+ // Started once we've parsed the body (need userMessageId to stamp as
612+ // streamId). Every subsequent span (persistUserMessage,
613+ // createRunSegment, the whole SSE stream, etc.) nests under this
614+ // root via AsyncLocalStorage / explicit propagation, and the stream's
615+ // terminal code path calls finish() when the request actually ends.
616+ // Errors thrown from the handler before the stream starts are
617+ // finished here in the catch below.
622618 let otelRoot : ReturnType < typeof startCopilotOtelRoot > | undefined
619+ // `requestId` is the canonical logical ID for this HTTP request —
620+ // same value that flows into `request.id`/`sim.request_id` span
621+ // attributes, the persisted `msg.requestId`, and eventually the
622+ // Grafana trace-ID search box. Derived from otelRoot.requestId (= the
623+ // OTel trace ID of the root span) as soon as that's created. Stays
624+ // empty only in the narrow window before otelRoot is set — errors in
625+ // that window can't be correlated to any trace anyway, and their log
626+ // line carries the error message + stack which is the actually
627+ // useful info.
628+ let requestId = ''
623629 const executionId = crypto . randomUUID ( )
624630 const runId = crypto . randomUUID ( )
625631
@@ -635,7 +641,11 @@ export async function handleUnifiedChatPost(req: NextRequest) {
635641 userMessageId = body . userMessageId || crypto . randomUUID ( )
636642
637643 otelRoot = startCopilotOtelRoot ( {
638- requestId : tracker . requestId ,
644+ // No explicit requestId — startCopilotOtelRoot derives it from
645+ // the span's OTel trace ID so `msg.requestId` on the UI side
646+ // ends up being the same value Grafana uses. See the scope
647+ // doc-comment and the call site for why this is the desired
648+ // direction of the unification.
639649 streamId : userMessageId ,
640650 executionId,
641651 runId,
@@ -646,6 +656,12 @@ export async function handleUnifiedChatPost(req: NextRequest) {
646656 // by setInputMessages above.
647657 userMessagePreview : body . message ,
648658 } )
659+ // Promote the OTel-derived ID to the handler-level `requestId` so
660+ // every downstream consumer (logs, orchestrator, onComplete,
661+ // onError, persisted assistant message) uses the same value.
662+ if ( otelRoot . requestId ) {
663+ requestId = otelRoot . requestId
664+ }
649665 // Emit `gen_ai.input.messages` on the root agent span for OTel
650666 // GenAI spec compliance (Honeycomb's Gen AI view keys off this).
651667 // Gated on OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT
@@ -800,7 +816,7 @@ export async function handleUnifiedChatPost(req: NextRequest) {
800816 message : body . message ,
801817 workspaceId,
802818 chatId : actualChatId ,
803- requestId : tracker . requestId ,
819+ requestId,
804820 } ) ,
805821 otelRoot ! . context
806822 )
@@ -908,7 +924,7 @@ export async function handleUnifiedChatPost(req: NextRequest) {
908924 message : body . message ,
909925 titleModel : branch . titleModel ,
910926 ...( branch . titleProvider ? { titleProvider : branch . titleProvider } : { } ) ,
911- requestId : tracker . requestId ,
927+ requestId,
912928 workspaceId,
913929 otelRoot : otelRoot ! ,
914930 orchestrateOptions : {
@@ -925,15 +941,15 @@ export async function handleUnifiedChatPost(req: NextRequest) {
925941 onComplete : buildOnComplete ( {
926942 chatId : actualChatId ,
927943 userMessageId,
928- requestId : tracker . requestId ,
944+ requestId,
929945 workspaceId,
930946 notifyWorkspaceStatus : branch . notifyWorkspaceStatus ,
931947 otelRoot,
932948 } ) ,
933949 onError : buildOnError ( {
934950 chatId : actualChatId ,
935951 userMessageId,
936- requestId : tracker . requestId ,
952+ requestId,
937953 workspaceId,
938954 notifyWorkspaceStatus : branch . notifyWorkspaceStatus ,
939955 } ) ,
@@ -970,7 +986,7 @@ export async function handleUnifiedChatPost(req: NextRequest) {
970986 )
971987 }
972988
973- logger . error ( `[${ tracker . requestId } ] Error handling unified chat request` , {
989+ logger . error ( `[${ requestId } ] Error handling unified chat request` , {
974990 error : error instanceof Error ? error . message : 'Unknown error' ,
975991 stack : error instanceof Error ? error . stack : undefined ,
976992 } )
0 commit comments