[GH-ISSUE #4170] [langchain]: Bug: HumanInTheLoopMiddleware — camelCase/snake_case mismatch on "edit" decisions #2784

Open
opened 2026-06-05 17:26:46 -04:00 by yindo · 1 comment
Owner

Originally created by @DannyWhyze on GitHub (May 26, 2026).
Original GitHub issue: https://github.com/langchain-ai/docs/issues/4170

Type of issue

issue / bug

Language

Python

Description

Affected Components

  • Python: langchain package — HumanInTheLoopMiddleware._process_decision()
  • TypeScript: langchain npm package — EditDecision interface
  • API: langgraph_apicommand.py:map_cmd()
  • Template: human-in-the-loop example project

Error

When submitting an "edit" decision via the HumanInTheLoopMiddleware, the Python middleware raises KeyError: 'edited_action'.

Discovery

The bug was first encountered during Python implementation when the edit flow consistently failed. Cross-checking against the official example at https://docs.langchain.com/oss/python/langchain/frontend/human-in-the-loop confirmed the same issue exists in the reference implementation — the TypeScript frontend sends editedAction (camelCase) but the Python backend middleware expects edited_action (snake_case), with no conversion layer between them.

Root Cause

  1. The TypeScript type definition (langchain npm) defines EditDecision.editedAction (camelCase)
  2. The Python type definition (langchain pip) defines EditDecision.edited_action (snake_case)
  3. The API layer (langgraph_api/command.py:30) passes the resume payload through unchanged: resume=cmd.get("resume")no camelCase → snake_case conversion takes place
  4. The Python middleware (human_in_the_loop.py:274) accesses decision["edited_action"] and fails because the key is editedAction

Affected Code Locations

Python Middleware — expects snake_case

# .venv/.../langchain/agents/middleware/human_in_the_loop.py:274
edited_action = decision["edited_action"]

TypeScript Interface — uses camelCase

# node_modules/langchain/dist/agents/middleware/hitl.d.cts:185
interface EditDecision {
  type: "edit";
  editedAction: Action;
}

API Layer — no conversion

# .venv/.../langgraph_api/command.py:30
return Command(
    ...
    resume=cmd.get("resume"),  # passed through as-is
)

Official Template also affected

# human-in-the-loop/packages/frontend/src/patterns/human-in-the-loop/preview.tsx:86
editedAction: { name: originalAction.name, args: editedArgs },

Workaround (applied in our project)

preview.tsx:87: changed editedActionedited_action (send snake_case from the frontend)

Suggested Fixes (for maintainers)

Option A (recommended): langgraph_api/command.py:map_cmd() converts known camelCase keys in the resume payload to snake_case.

Option B: Update TypeScript types and templates to use snake_case.

Option C: Python middleware accepts both variants (decision.get("edited_action") or decision.get("editedAction")).

Originally created by @DannyWhyze on GitHub (May 26, 2026). Original GitHub issue: https://github.com/langchain-ai/docs/issues/4170 ### Type of issue issue / bug ### Language Python ### Description ## Affected Components - **Python:** `langchain` package — `HumanInTheLoopMiddleware._process_decision()` - **TypeScript:** `langchain` npm package — `EditDecision` interface - **API:** `langgraph_api` — `command.py:map_cmd()` - **Template:** `human-in-the-loop` example project ## Error When submitting an `"edit"` decision via the HumanInTheLoopMiddleware, the Python middleware raises `KeyError: 'edited_action'`. ## Discovery The bug was first encountered during Python implementation when the edit flow consistently failed. Cross-checking against the official example at https://docs.langchain.com/oss/python/langchain/frontend/human-in-the-loop confirmed the same issue exists in the reference implementation — the TypeScript frontend sends `editedAction` (camelCase) but the Python backend middleware expects `edited_action` (snake_case), with no conversion layer between them. ## Root Cause 1. The **TypeScript type definition** (`langchain` npm) defines `EditDecision.editedAction` (camelCase) 2. The **Python type definition** (`langchain` pip) defines `EditDecision.edited_action` (snake_case) 3. The **API layer** (`langgraph_api/command.py:30`) passes the `resume` payload through unchanged: `resume=cmd.get("resume")` — **no** camelCase → snake_case conversion takes place 4. The Python middleware (`human_in_the_loop.py:274`) accesses `decision["edited_action"]` and fails because the key is `editedAction` ## Affected Code Locations ### Python Middleware — expects snake_case ``` # .venv/.../langchain/agents/middleware/human_in_the_loop.py:274 edited_action = decision["edited_action"] ``` ### TypeScript Interface — uses camelCase ``` # node_modules/langchain/dist/agents/middleware/hitl.d.cts:185 interface EditDecision { type: "edit"; editedAction: Action; } ``` ### API Layer — no conversion ``` # .venv/.../langgraph_api/command.py:30 return Command( ... resume=cmd.get("resume"), # passed through as-is ) ``` ### Official Template also affected ``` # human-in-the-loop/packages/frontend/src/patterns/human-in-the-loop/preview.tsx:86 editedAction: { name: originalAction.name, args: editedArgs }, ``` ## Workaround (applied in our project) `preview.tsx:87`: changed `editedAction` → `edited_action` (send snake_case from the frontend) ## Suggested Fixes (for maintainers) **Option A** (recommended): `langgraph_api/command.py:map_cmd()` converts known camelCase keys in the resume payload to snake_case. **Option B**: Update TypeScript types and templates to use snake_case. **Option C**: Python middleware accepts both variants (`decision.get("edited_action") or decision.get("editedAction")`).
yindo added the langchainexternal labels 2026-06-05 17:26:46 -04:00
Author
Owner

@AayushShah-904 commented on GitHub (Jun 4, 2026):

Hi! I've identified the root cause of this bug — in libs/langchain_v1/langchain/agents/middleware/human_in_the_loop.py line 311, the middleware accesses decision["edited_action"] but the TypeScript frontend sends editedAction (camelCase) with no conversion layer. I've implemented a fix that handles both variants and written a passing test. Could a maintainer assign this issue to me and confirm I can submit a PR to langchain-ai/langchain

<!-- gh-comment-id:4627592424 --> @AayushShah-904 commented on GitHub (Jun 4, 2026): Hi! I've identified the root cause of this bug — in libs/langchain_v1/langchain/agents/middleware/human_in_the_loop.py line 311, the middleware accesses decision["edited_action"] but the TypeScript frontend sends editedAction (camelCase) with no conversion layer. I've implemented a fix that handles both variants and written a passing test. Could a maintainer assign this issue to me and confirm I can submit a PR to langchain-ai/langchain
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: langchain-ai/docs#2784