Skip to content

Fix: Handle ToolNode in tools iteration for LangGraph compatibility#3946

Open
Utkarshkhandka wants to merge 3 commits intotraceloop:mainfrom
Utkarshkhandka:fix/toolnode-not-iterable
Open

Fix: Handle ToolNode in tools iteration for LangGraph compatibility#3946
Utkarshkhandka wants to merge 3 commits intotraceloop:mainfrom
Utkarshkhandka:fix/toolnode-not-iterable

Conversation

@Utkarshkhandka
Copy link
Copy Markdown

@Utkarshkhandka Utkarshkhandka commented Apr 2, 2026

Problem

When using langgraph-supervisor, create_react_agent may pass a ToolNode object as tools. Since ToolNode is not directly iterable, the current loop in patch.py raises:

Fixes #3921

TypeError: 'ToolNode' object is not iterable

Root Cause

ToolNode stores tools internally via tools_by_name (a dict), not as a direct iterable — so for tool in tools fails.

Fix

if hasattr(tools, "tools_by_name"):
    iterable_tools = tools.tools_by_name.values()
elif isinstance(tools, (list, tuple, set)):
    iterable_tools = tools
else:
    iterable_tools = [tools]
 
for tool in iterable_tools:
    tool_def = _extract_tool_definition(tool)

Handles ToolNode, standard iterables, and single tool objects — fully backward compatible.

Testing

python -m pytest tests/test_langgraph.py -k "not invoke"
# 11 passed, 4 deselected

Deselected tests require a live OpenAI key — unrelated to this fix.

Impact

  • Fixes crash with langgraph-supervisor + create_react_agent
  • Backward compatible with existing iterable inputs
  • Minimal, focused change — no unrelated files modified

  • I have added tests that cover my changes.
  • If adding a new instrumentation or changing an existing one, I've added screenshots from some observability platform showing the change.
  • PR name follows conventional commits format: feat(instrumentation): ... or fix(instrumentation): ....
  • (If applicable) I have updated the documentation accordingly.

Summary by CodeRabbit

  • Bug Fixes
    • Improved agent tool input handling to support various formats (lists, tuples, sets, named collections, and single objects) for broader compatibility.
    • Only records tool definitions when actual tool metadata is present, reducing noise and improving telemetry clarity.

@CLAassistant
Copy link
Copy Markdown

CLAassistant commented Apr 2, 2026

CLA assistant check
All committers have signed the CLA.

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Apr 2, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 557c5190-23f7-46f4-8387-0e82da6cf535

📥 Commits

Reviewing files that changed from the base of the PR and between 9228d8c and eefffb2.

📒 Files selected for processing (1)
  • packages/opentelemetry-instrumentation-langchain/opentelemetry/instrumentation/langchain/patch.py

📝 Walkthrough

Walkthrough

Adjusted the agent-creation wrapper to robustly extract tool definitions from various tools shapes: uses tools.tools_by_name.values() if present, accepts native iterables, or wraps a single object as an iterable; only sets GenAI tool-definition attribute when at least one definition is collected.

Changes

Cohort / File(s) Summary
Tool Input Normalization
packages/opentelemetry-instrumentation-langchain/opentelemetry/instrumentation/langchain/patch.py
create_agent_wrapper now derives an iterable from tools by preferring tools.tools_by_name.values() when available, accepting list/tuple/set, or wrapping a single non-None object. Iterates to extract tool definitions and sets GenAIAttributes.GEN_AI_TOOL_DEFINITIONS only if definitions were collected. Inline comment on LangChain system_prompt naming updated.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

Possibly related issues

Poem

🐰 I hopped through code with careful feet,

Found tools tucked where lists should meet,
I pulled their names from nodes and sets,
No more crashes, no more frets,
Hooray — the agent’s flow is neat!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 50.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the main change: handling ToolNode in tools iteration for LangGraph compatibility, which directly addresses the core issue.
Linked Issues check ✅ Passed The PR fully addresses the requirements from issue #3921: it detects ToolNode objects with tools_by_name attribute, iterates their values, and handles standard iterables and single objects.
Out of Scope Changes check ✅ Passed All changes are focused on the tools iteration logic in create_agent_wrapper. The only modification is the comment clarification, which is a minor documentation fix within scope.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In
`@packages/opentelemetry-instrumentation-langchain/opentelemetry/instrumentation/langchain/patch.py`:
- Around line 489-498: Fix the indentation and remove the duplicate function
call: ensure the branch that sets iterable_tools uses consistent indentation
(align the "iterable_tools =" lines under the initial if/elif/else block so
there are no stray or over-indented spaces) and eliminate the redundant call to
_extract_tool_definition by keeping a single assignment to tool_def inside the
for loop (for tool in iterable_tools: tool_def =
_extract_tool_definition(tool)). This touches the symbols tools, tools_by_name,
iterable_tools, _extract_tool_definition, tool_def, and the for tool in
iterable_tools loop.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 44ebe40d-aa4c-47d3-9157-3d155ca88773

📥 Commits

Reviewing files that changed from the base of the PR and between 0a25803 and 0546abf.

📒 Files selected for processing (1)
  • packages/opentelemetry-instrumentation-langchain/opentelemetry/instrumentation/langchain/patch.py

Corrected comment punctuation and adjusted indentation for better readability. Streamlined tool extraction logic.
Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
packages/opentelemetry-instrumentation-langchain/opentelemetry/instrumentation/langchain/patch.py (1)

487-504: ⚠️ Potential issue | 🔴 Critical

Critical: Indentation errors will cause SyntaxError at import time.

The code has multiple indentation issues that break the module:

  1. Lines 489, 491, 493: The if hasattr/elif/else block is at 12 spaces (same level as if tools: on line 487), making it execute regardless of whether tools is truthy.
  2. Line 492: Has 17 spaces (one extra) instead of 20.
  3. Line 496: The for loop is incorrectly nested inside the else: block.
  4. Lines 498, 500: Have 17 spaces (mismatched indentation) — Ruff confirms these as invalid-syntax.

This will raise IndentationError when the module is imported, completely breaking the instrumentation.

🐛 Proposed fix for indentation and structure
             if tools:
                 tool_definitions = []
-            if hasattr(tools, "tools_by_name"):
-                iterable_tools = tools.tools_by_name.values()
-            elif isinstance(tools, (list, tuple, set)):
-                 iterable_tools = tools
-            else:
-                iterable_tools = [tools]
-
-                for tool in iterable_tools:
+                if hasattr(tools, "tools_by_name"):
+                    iterable_tools = tools.tools_by_name.values()
+                elif isinstance(tools, (list, tuple, set)):
+                    iterable_tools = tools
+                else:
+                    iterable_tools = [tools]
+
+                for tool in iterable_tools:
                     tool_def = _extract_tool_definition(tool)
-                 if tool_def:
-                    tool_definitions.append(tool_def)
-                 if tool_definitions:
+                    if tool_def:
+                        tool_definitions.append(tool_def)
+                if tool_definitions:
                     span.set_attribute(
                         GenAIAttributes.GEN_AI_TOOL_DEFINITIONS,
                         json.dumps(tool_definitions)
                     )
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@packages/opentelemetry-instrumentation-langchain/opentelemetry/instrumentation/langchain/patch.py`
around lines 487 - 504, The block handling `tools` has broken indentation
causing a SyntaxError; nest the `if hasattr(tools, "tools_by_name")` / `elif
isinstance(tools, ...)` / `else` inside the `if tools:` branch so they only run
when `tools` is truthy, ensure the `for tool in iterable_tools:` loop is at the
same indentation level as those branches (not inside the `else`), and fix the
misaligned lines that append `tool_def` (using `_extract_tool_definition(tool)`)
and the final `span.set_attribute(GenAIAttributes.GEN_AI_TOOL_DEFINITIONS,
json.dumps(tool_definitions))` so they execute after the loop when
`tool_definitions` is non-empty; reference `tools`, `_extract_tool_definition`,
`tool_definitions`, `span.set_attribute`, and
`GenAIAttributes.GEN_AI_TOOL_DEFINITIONS` to locate and correct the indentation.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Outside diff comments:
In
`@packages/opentelemetry-instrumentation-langchain/opentelemetry/instrumentation/langchain/patch.py`:
- Around line 487-504: The block handling `tools` has broken indentation causing
a SyntaxError; nest the `if hasattr(tools, "tools_by_name")` / `elif
isinstance(tools, ...)` / `else` inside the `if tools:` branch so they only run
when `tools` is truthy, ensure the `for tool in iterable_tools:` loop is at the
same indentation level as those branches (not inside the `else`), and fix the
misaligned lines that append `tool_def` (using `_extract_tool_definition(tool)`)
and the final `span.set_attribute(GenAIAttributes.GEN_AI_TOOL_DEFINITIONS,
json.dumps(tool_definitions))` so they execute after the loop when
`tool_definitions` is non-empty; reference `tools`, `_extract_tool_definition`,
`tool_definitions`, `span.set_attribute`, and
`GenAIAttributes.GEN_AI_TOOL_DEFINITIONS` to locate and correct the indentation.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 95c78da3-f0c1-4a4b-91ff-cc9c55d214ac

📥 Commits

Reviewing files that changed from the base of the PR and between 0546abf and 9228d8c.

📒 Files selected for processing (1)
  • packages/opentelemetry-instrumentation-langchain/opentelemetry/instrumentation/langchain/patch.py

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.

TypeError: 'ToolNode' object is not iterable in patch.py when using langgraph-supervisor with create_react_agent

2 participants