Skip to content

fix: guard add_tool_field against an orphaned tool_id - #3740

Open
AmirF194 wants to merge 1 commit into
ModelEngine-Group:developfrom
AmirF194:fix/3650-add-tool-field-orphaned-tool-none-guard
Open

fix: guard add_tool_field against an orphaned tool_id#3740
AmirF194 wants to merge 1 commit into
ModelEngine-Group:developfrom
AmirF194:fix/3650-add-tool-field-orphaned-tool-none-guard

Conversation

@AmirF194

Copy link
Copy Markdown

Root cause

ToolInstance.tool_id (backend/database/db_models.py) has no foreign key on
ToolInfo.tool_id, so a ToolInstance row can outlive the ToolInfo row it
points to. add_tool_field does not account for that: query.first() can
return None, and the very next line dereferences tool.params, which is
exactly the traceback in #3650:

File backend/database/tool_db.py, line 442, in search_tools_for_sub_agent
AttributeError: 'NoneType' object has no attribute 'params'

The issue's own suggested fix, tool_params = tool.params or [], does not
fix this: the crash happens evaluating tool.params when tool itself is
None, one step before that fallback would ever run. I confirmed this by
reproducing both shapes in isolation: a None tool raises the reported
AttributeError, while a tool.params of None raises a different
TypeError on the iteration below it.

Fix

  • add_tool_field returns None when no ToolInfo row matches the given
    tool_id, instead of crashing.
  • Its caller, search_tools_for_sub_agent, skips a None result so one
    orphaned tool reference no longer aborts the whole agent run.
  • tool.params also defaults to [], covering a ToolInfo row that
    legitimately has no configured params (the column is nullable), which
    matches the issue title.

Verification

  • New regression tests in test/backend/database/test_tool_db.py
    (test_add_tool_field_missing_tool_info_returns_none,
    test_add_tool_field_null_params_defaults_to_empty_list,
    test_search_tools_for_sub_agent_skips_orphaned_tool) fail on unmodified
    develop with the exact reported AttributeError and pass on this branch.
  • Full test/backend/database and test/backend/agents suites (1670 tests,
    includes test_create_agent_info.py, which exercises the calling path)
    pass via test/run_all_test.py in the CI image (python:3.11,
    uv sync --extra test, uv pip install -e "../sdk[dev]").
  • Coverage confirms both new branches are exercised (95% on tool_db.py,
    no changed line in the miss list).
  • Not checked: an actual Postgres row with tool_id missing end to end;
    the mock reproduces the exact code path from the traceback instead.

Fixes #3650

ToolInstance.tool_id carries no foreign key, so a ToolInstance can
outlive the ToolInfo row it points to. add_tool_field's query.first()
then returns None, and the next line dereferences tool.params,
crashing agent execution with AttributeError: 'NoneType' object has
no attribute 'params'. Return None for a missing tool and skip it in
search_tools_for_sub_agent's caller loop. Also default tool.params to
an empty list for a tool that legitimately has none configured.
Copilot AI lite review requested due to automatic review settings August 22, 2026 07:29

Copilot AI 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.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

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] tool_db.py add_tool_field: NoneType object has no attribute 'params' when tool.params is NULL

2 participants