Skip to content

Bug: langchain-anthropic file_tool callback never emits args["old_path"] but _handle_rename reads it -> KeyError on every rename #38465

Description

@Fr3ya

Submission checklist

  • This is a bug, not a usage question.
  • I added a clear and descriptive title that summarizes this issue.
  • I used the GitHub search to find a similar question and didn't find it.
  • I am sure that this is a bug in LangChain rather than my code.
  • The bug is not resolved by updating to the latest stable version of LangChain (or the specific integration package).
  • This is not related to the langchain-community package.
  • I posted a self-contained, minimal, reproducible example. A maintainer can copy it and run it AS IS.

Package (Required)

  • langchain
  • langchain-openai
  • langchain-anthropic
  • langchain-classic
  • langchain-core
  • langchain-model-profiles
  • langchain-tests
  • langchain-text-splitters
  • langchain-chroma
  • langchain-deepseek
  • langchain-exa
  • langchain-fireworks
  • langchain-groq
  • langchain-huggingface
  • langchain-mistralai
  • langchain-nomic
  • langchain-ollama
  • langchain-openrouter
  • langchain-perplexity
  • langchain-qdrant
  • langchain-xai
  • Other / not sure / general

Related Issues / PRs

No response

Reproduction Steps / Example Code (Python)

import tempfile
from pathlib import Path
from langchain_anthropic.middleware.anthropic_tools import (
    StateClaudeTextEditorMiddleware,
    StateClaudeMemoryMiddleware,
    FilesystemClaudeTextEditorMiddleware,
    FilesystemClaudeMemoryMiddleware,
)

# Site 1: state-backed text editor
mw = StateClaudeTextEditorMiddleware()
state = {mw.state_key: {"/some/old.txt": {
    "content": "hi",
    "created_at": "2026-01-01T00:00:00+00:00",
    "modified_at": "2026-01-01T00:00:00+00:00",
}}}
try:
    mw._handle_rename(
        {"path": "/some/old.txt", "new_path": "/some/new.txt"}, state, "tc-1"
    )
except KeyError as e:
    print("state textedit:", repr(e))

# Site 2: state-backed memory (same base class)
mw = StateClaudeMemoryMiddleware()
state = {mw.state_key: {"/memories/old.txt": {
    "content": "hi",
    "created_at": "2026-01-01T00:00:00+00:00",
    "modified_at": "2026-01-01T00:00:00+00:00",
}}}
try:
    mw._handle_rename(
        {"path": "/memories/old.txt", "new_path": "/memories/new.txt"},
        state, "tc-2",
    )
except KeyError as e:
    print("state memory:", repr(e))

# Site 3: filesystem-backed text editor (sibling base class)
with tempfile.TemporaryDirectory() as root:
    (Path(root) / "old.txt").write_text("hi")
    mw = FilesystemClaudeTextEditorMiddleware(root_path=root)
    try:
        mw._handle_rename(
            {"path": "/old.txt", "new_path": "/new.txt"}, "tc-3"
        )
    except KeyError as e:
        print("fs textedit:", repr(e))

# Site 4: filesystem-backed memory
with tempfile.TemporaryDirectory() as root:
    (Path(root) / "memories").mkdir()
    (Path(root) / "memories" / "old.txt").write_text("hi")
    mw = FilesystemClaudeMemoryMiddleware(root_path=root)
    try:
        mw._handle_rename(
            {"path": "/memories/old.txt", "new_path": "/memories/new.txt"},
            "tc-4",
        )
    except KeyError as e:
        print("fs memory:", repr(e))

# Expected (and observed) output -- CONFIRMED BUG:
#   state textedit: KeyError('old_path')
#   state memory:   KeyError('old_path')
#   fs textedit:    KeyError('old_path')
#   fs memory:      KeyError('old_path')

Error Message and Stack Trace (if applicable)

Traceback (most recent call last):
  File "repro.py", line ..., in <module>
    mw._handle_rename(
  File ".../langchain_anthropic/middleware/anthropic_tools.py", line 558, in _handle_rename
    old_path = args["old_path"]
               ~~~~^^^^^^^^^^^^
KeyError: 'old_path'



The KeyError fires on the first executable line of `_handle_rename`, before any state mutation or filesystem call. When this happens inside a LangGraph tool node the exception is not caught by the surrounding `except (ValueError, FileNotFoundError[, PermissionError]) as e:` block (lines [282](https://github.com/langchain-ai/langchain/blob/9d14a5e06d98355e5c0eccd0736b961fbe419f87/libs/partners/anthropic/langchain_anthropic/middleware/anthropic_tools.py#L282) and [774](https://github.com/langchain-ai/langchain/blob/9d14a5e06d98355e5c0eccd0736b961fbe419f87/libs/partners/anthropic/langchain_anthropic/middleware/anthropic_tools.py#L774)), so it propagates into the Pregel runner.

Description

The file_tool callback in _StateClaudeFileToolMiddleware.__init__ (anthropic_tools.py#L243-L253) builds an args dict containing path, file_text, old_str, new_str, insert_line, new_path, and view_range, then dispatches by command. The handler reached on the rename branch is _handle_rename (anthropic_tools.py#L554-L558), whose first executable line is old_path = args["old_path"] -- a key the producer never sets. The same producer/consumer pair is duplicated verbatim in _FilesystemClaudeFileToolMiddleware (producer at L745-L755, consumer at L1055-L1057), so all four user-facing subclasses (StateClaudeTextEditorMiddleware, StateClaudeMemoryMiddleware, FilesystemClaudeTextEditorMiddleware, FilesystemClaudeMemoryMiddleware) crash on every rename. This is graph-native because the args dict serves as a per-tool-call carrier between framework-side dispatch and a framework-internal handler; a direct Python call would never assemble the malformed dict in the first place. Expected: rename succeeds and the handler returns a Command updating state or filesystem. Actual: KeyError('old_path') escapes the tool node.

Suggested fix (smallest patch is producer-side; one line per producer):

# In file_tool(...) inside _StateClaudeFileToolMiddleware.__init__ (line ~252)
 if new_path is not None:
     args["new_path"] = new_path
+if command == "rename":
+    args["old_path"] = path

Apply the same one-liner to _FilesystemClaudeFileToolMiddleware.__init__ around line 754. Consider adding KeyError to the surrounding except (...) whitelist so future producer/consumer key drifts return as a string tool error instead of escaping.

System Info

OS: Linux

Metadata

Metadata

Assignees

No one assigned

    Labels

    anthropic`langchain-anthropic` package issues & PRsbugRelated to a bug, vulnerability, unexpected error with an existing featureexternal

    Type

    No fields configured for Bug.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions