fix(server-utils): Record LangGraph span I/O for non-MessagesAnnotation state - #23315
fix(server-utils): Record LangGraph span I/O for non-MessagesAnnotation state#23315zkasuran wants to merge 1 commit into
Conversation
…on state instrumentCompiledGraphInvoke read the graph input and output through `args[0].messages` and `result.messages`. Those keys only exist for graphs built on MessagesAnnotation, so a graph with a custom state annotation had its `invoke_agent` span input and output silently left empty with no error. Keep the existing message path unchanged. When there is no `messages` array, fall back to serializing the whole input and output state onto `gen_ai.input.messages` and `gen_ai.response.text`, wrapped as a single role/content message so the attribute stays a valid chat array (the same shape LangChain LLM prompts already use). A null input still records nothing. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Want reviews to match your repository better? Bugbot Learning can learn team-specific rules from PR activity. A team admin can enable Learning in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit e194f28. Configure here.
| } else if (inputState && typeof inputState === 'object') { | ||
| span.setAttributes({ | ||
| [GEN_AI_INPUT_MESSAGES]: stringify([{ role: 'user', content: stringify(inputState) }]), | ||
| }); |
There was a problem hiding this comment.
Empty messages misrouted to fallback
Low Severity
inputMessages is normalized with ?? [], then the MessagesAnnotation path is chosen only when length > 0. An explicit empty messages array therefore falls through to the custom-state branch and is recorded as a synthetic user message wrapping the whole input object. That diverges from the output helper, which uses Array.isArray and keeps empty messages on the chat path, and changes prior MessagesAnnotation behavior for empty inputs.
Reviewed by Cursor Bugbot for commit e194f28. Configure here.


instrumentLangGraphrecorded span input and output only when the graph state usesMessagesAnnotation. The input read didargs[0].messages ?? []and the output helper returned early whenresult.messageswas not an array, so a graph on a custom state annotation produced an emptygen_ai.input.messagesand nogen_ai.response.text. No error, just an emptyinvoke_agentspan in the AI Agents view.This keeps the
MessagesAnnotationpath unchanged. When there is nomessagesarray, it serializes the whole input and output state and records it, wrapped as a single{ role, content }message so the attribute stays a valid chat array (the conventionextractLLMRequestAttributesalready uses in this package). A null input (the resume case) records nothing rather than a misleading empty array. Serialization uses core's circular-safestringifyso an unusual state object cannot throw inside the span callback.Fixes #19628
AI assistance (Claude, Anthropic) was used in developing this change. The design, review and verification were done by the author. Verified locally: the
@sentry/server-utilsvitest suite (335 to 339 passing), the new tests failing before and passing after the fix,oxlint --type-aware,oxfmt --check,tscon the changed files, plus a real Google Gemini LangGraph run showing the span input and output empty before and populated after.