Skip to content

fix(langgraph): map LangChain tool status back onto AG-UI ToolMessage.error - #2316

Merged
BenTaylorDev merged 1 commit into
ag-ui-protocol:mainfrom
TheSeydiCharyyev:fix/2305-langgraph-reverse-tool-status
Aug 10, 2026
Merged

fix(langgraph): map LangChain tool status back onto AG-UI ToolMessage.error#2316
BenTaylorDev merged 1 commit into
ag-ui-protocol:mainfrom
TheSeydiCharyyev:fix/2305-langgraph-reverse-tool-status

Conversation

@TheSeydiCharyyev

Copy link
Copy Markdown
Contributor

Fixes #2305.

Problem

#2263 fixed the forward direction (AG-UI → LangChain): an incoming ToolMessage.error becomes status="error". The reverse direction was still lossy — langchainMessagesToAgui / langchain_messages_to_agui never read status, so a LangChain tool result with status="error" came back to the client with error unset.

This undoes #2263's fix for any flow where the client's message list seeds the next run (a fresh thread, a stateless replay, a snapshot-then-resend): the client re-converts through the forward path with error unset, and silently gets status="success" again.

Fix

Map status == "error" back onto AG-UI's error in both adapters:

  • TypeScript — integrations/langgraph/typescript/src/utils.ts, the tool branch of langchainMessagesToAgui.
  • Python — integrations/langgraph/python/ag_ui_langgraph/utils.py, the ToolMessage branch of langchain_messages_to_agui.

The sentinel, and the honest contract

Per the standard agreed on #2306, the forward direction is flag-only, so the original error text is not recoverable here. error therefore carries a fixed sentinel ("error") rather than a synthesized-but-plausible string, so a client can tell "the agent reported failure" apart from "the agent reported this failure text". The flag is durable across a round trip; the text is best-effort. There is no portable free-text slot to preserve it (Bedrock's toolResult has none, AI SDK's providerOptions is provider-namespaced).

I picked a bare "error" sentinel to be obviously synthetic — open to a different marker if you prefer.

Tests

A test is added on each side, next to the existing reverse-direction tool-message tests.

  • TS: message-conversion.test.ts — full package suite green, 268 tests.
  • Python: test_message_conversion.pyunittest discover tests green, 385 tests.

….error

The reverse conversion (langchainMessagesToAgui / langchain_messages_to_agui) dropped the LangChain tool result status, so a status='error' tool message came back to the client with error unset. After ag-ui-protocol#2263 fixed the forward direction, a client whose message list seeds the next run (fresh thread, stateless replay, snapshot-then-resend) re-converted with error unset and silently produced status='success' again, undoing the fix.

Map status == 'error' back onto AG-UI's error in both adapters. The forward direction is flag-only, so the original text is not recoverable here; error carries a fixed sentinel so a client can tell a reported failure apart from a reported failure text. Flag durable, text best-effort, per the standard agreed on ag-ui-protocol#2306. A test is added on each side.

Fixes ag-ui-protocol#2305
@TheSeydiCharyyev
TheSeydiCharyyev requested a review from a team as a code owner August 5, 2026 04:24
@github-actions

Copy link
Copy Markdown
Contributor

Python Preview Packages

Version 0.0.0.dev1785903854 published to TestPyPI.

Warning: These packages are built from contributor code that may not yet have been vetted for correctness or security. Install at your own risk and do not use in production.

Install with uv

Add the TestPyPI index to your pyproject.toml:

[[tool.uv.index]]
name = "testpypi"
url = "https://test.pypi.org/simple/"
explicit = true

Then install the packages you need:

# Core SDK
uv add 'ag-ui-protocol==0.0.0.dev1785903854' --index testpypi

# Integrations (each already depends on the matching ag-ui-protocol preview)
uv add 'ag-ui-langgraph==0.0.0.dev1785903854' --index testpypi
uv add 'ag-ui-crewai==0.0.0.dev1785903854' --index testpypi
# NOTE: ag-ui-agent-spec depends on pyagentspec (git-only, not on PyPI).
# You will need to install pyagentspec separately from its git repo.
uv add 'ag-ui-agent-spec==0.0.0.dev1785903854' --index testpypi
uv add 'ag_ui_adk==0.0.0.dev1785903854' --index testpypi
uv add 'ag_ui_strands==0.0.0.dev1785903854' --index testpypi

Install with pip

pip install \
  --index-url https://test.pypi.org/simple/ \
  --extra-index-url https://pypi.org/simple/ \
  ag-ui-protocol==0.0.0.dev1785903854

Use --extra-index-url https://pypi.org/simple/ so pip can resolve
transitive dependencies (pydantic, fastapi, etc.) from real PyPI.


Commit: 183589c

@pkg-pr-new

pkg-pr-new Bot commented Aug 10, 2026

Copy link
Copy Markdown

Open in StackBlitz

@ag-ui/a2a-middleware

pnpm add https://pkg.pr.new/ag-ui-protocol/ag-ui/@ag-ui/a2a-middleware@2316

@ag-ui/a2ui-middleware

pnpm add https://pkg.pr.new/ag-ui-protocol/ag-ui/@ag-ui/a2ui-middleware@2316

@ag-ui/event-throttle-middleware

pnpm add https://pkg.pr.new/ag-ui-protocol/ag-ui/@ag-ui/event-throttle-middleware@2316

@ag-ui/mcp-apps-middleware

pnpm add https://pkg.pr.new/ag-ui-protocol/ag-ui/@ag-ui/mcp-apps-middleware@2316

@ag-ui/mcp-middleware

pnpm add https://pkg.pr.new/ag-ui-protocol/ag-ui/@ag-ui/mcp-middleware@2316

@ag-ui/a2a

pnpm add https://pkg.pr.new/ag-ui-protocol/ag-ui/@ag-ui/a2a@2316

@ag-ui/adk

pnpm add https://pkg.pr.new/ag-ui-protocol/ag-ui/@ag-ui/adk@2316

@ag-ui/ag2

pnpm add https://pkg.pr.new/ag-ui-protocol/ag-ui/@ag-ui/ag2@2316

@ag-ui/agno

pnpm add https://pkg.pr.new/ag-ui-protocol/ag-ui/@ag-ui/agno@2316

@ag-ui/aws-strands

pnpm add https://pkg.pr.new/ag-ui-protocol/ag-ui/@ag-ui/aws-strands@2316

@ag-ui/claude-agent-sdk

pnpm add https://pkg.pr.new/ag-ui-protocol/ag-ui/@ag-ui/claude-agent-sdk@2316

@ag-ui/claude-managed-agents

pnpm add https://pkg.pr.new/ag-ui-protocol/ag-ui/@ag-ui/claude-managed-agents@2316

@ag-ui/crewai

pnpm add https://pkg.pr.new/ag-ui-protocol/ag-ui/@ag-ui/crewai@2316

@ag-ui/langchain

pnpm add https://pkg.pr.new/ag-ui-protocol/ag-ui/@ag-ui/langchain@2316

@ag-ui/langgraph

pnpm add https://pkg.pr.new/ag-ui-protocol/ag-ui/@ag-ui/langgraph@2316

@ag-ui/llamaindex

pnpm add https://pkg.pr.new/ag-ui-protocol/ag-ui/@ag-ui/llamaindex@2316

@ag-ui/mastra

pnpm add https://pkg.pr.new/ag-ui-protocol/ag-ui/@ag-ui/mastra@2316

@ag-ui/pydantic-ai

pnpm add https://pkg.pr.new/ag-ui-protocol/ag-ui/@ag-ui/pydantic-ai@2316

@ag-ui/vercel-ai-sdk

pnpm add https://pkg.pr.new/ag-ui-protocol/ag-ui/@ag-ui/vercel-ai-sdk@2316

@ag-ui/watsonx

pnpm add https://pkg.pr.new/ag-ui-protocol/ag-ui/@ag-ui/watsonx@2316

@ag-ui/a2ui-toolkit

pnpm add https://pkg.pr.new/ag-ui-protocol/ag-ui/@ag-ui/a2ui-toolkit@2316

create-ag-ui-app

pnpm add https://pkg.pr.new/ag-ui-protocol/ag-ui/create-ag-ui-app@2316

@ag-ui/client

pnpm add https://pkg.pr.new/ag-ui-protocol/ag-ui/@ag-ui/client@2316

@ag-ui/core

pnpm add https://pkg.pr.new/ag-ui-protocol/ag-ui/@ag-ui/core@2316

@ag-ui/encoder

pnpm add https://pkg.pr.new/ag-ui-protocol/ag-ui/@ag-ui/encoder@2316

@ag-ui/proto

pnpm add https://pkg.pr.new/ag-ui-protocol/ag-ui/@ag-ui/proto@2316

commit: 58ee240

@BenTaylorDev BenTaylorDev left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approving. CI is green across all 42 checks on 58ee240d, and the change holds up under review.

What I verified beyond the diff:

  • The design is the one that was asked for. The PR cites the standard agreed on #2306, so I read both threads rather than taking the framing. The rule there is "set the framework's error flag from bool(error); fold the text in only where the target has no flag", and on #2305 the preference for the synthesized value was stated explicitly: a fixed sentinel over echoing content. This matches both.
  • Round trip against real langchain_core (1.2.13), not mocks. status="error"error='error' → back to status='error'; the success path stays error=Nonestatus='success'. The flag is durable in both directions.
  • Types line up. @langchain/langgraph-sdk@1.7.2 declares ToolMessage.status?: "error" | "success", and AG-UI's ToolMessage.error is optional string in both SDKs.
  • Merges clean with current main (78 commits on, nothing touching either edited file), and the full Python suite passes at 385.
  • No missed sites. The other four role: "tool" constructions in these adapters build TOOL_CALL_RESULT events, and ToolCallResultEvent has no error field in either SDK — nothing to carry there.

Two follow-ups worth tracking separately, neither blocking:

  1. That last point is a real limit on the fix's reach: the flag survives a MESSAGES_SNAPSHOT but the streamed TOOL_CALL_RESULT event has nowhere to put it. So "the flag is durable" holds for snapshots, not for the streaming path. Might deserve its own issue against the protocol rather than the adapter.
  2. The langgraph TypeScript package has no check-types script and vitest is transpile-only, so a green suite here isn't type evidence. Not a problem with this PR — just worth knowing what the signal does and doesn't cover.

Thanks for the care on this one, particularly for raising the text-slot question on #2306 first and mirroring the answer in both directions instead of guessing.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: LangGraph adapters drop ToolMessage status on the LangChain → AG-UI direction, so a tool failure is not durable across MESSAGES_SNAPSHOT

2 participants