Skip to content

fix(delegate): normalize tasks-as-string in delegate_task for batch mode#21957

Closed
liuhao1024 wants to merge 3 commits into
NousResearch:mainfrom
liuhao1024:fix/issue-21933-delegate-task-batch-json-string
Closed

fix(delegate): normalize tasks-as-string in delegate_task for batch mode#21957
liuhao1024 wants to merge 3 commits into
NousResearch:mainfrom
liuhao1024:fix/issue-21933-delegate-task-batch-json-string

Conversation

@liuhao1024

Copy link
Copy Markdown
Contributor

Summary

delegate_task batch mode silently fails when open-weight models (e.g. Qwen3.6-27B-FP8) emit the tasks array as a JSON-encoded string instead of a native list. The agent retries 4-5 times before falling back to single-task mode, making batch delegation unusable.

Root Cause

The generic coerce_tool_args() in model_tools.py attempts to parse string→array via _coerce_json(), but when the JSON string contains complex nested escaping (common with open-weight models), json.loads() fails silently. The string passes through unmodified, and delegate_task() sees isinstance(tasks, list) == False, falling through to the error path.

Fix

Add a normalization step at the top of delegate_task() that attempts to parse a string tasks parameter as JSON before the batch-mode logic. Returns a clear error message if parsing fails.

Files changed: tools/delegate_tool.py (+18 lines), tests/tools/test_delegate_tool.py (new, 124 lines)

Regression Coverage

  • test_tasks_as_json_string_is_parsed — JSON-encoded array string is correctly parsed
  • test_tasks_as_invalid_string_returns_error — unparseable string returns clear error
  • test_tasks_as_native_list_still_works — native list passes through unchanged
  • test_tasks_as_none_with_goal_works — single-task mode unaffected
  • test_tasks_empty_list_returns_error — empty list still returns error
  • test_tasks_string_with_nested_objects — complex nested JSON string parses correctly

Testing

All 6 new tests pass:

tests/tools/test_delegate_tool.py::TestDelegateTaskStringCoercion::test_tasks_as_json_string_is_parsed PASSED
tests/tools/test_delegate_tool.py::TestDelegateTaskStringCoercion::test_tasks_as_invalid_string_returns_error PASSED
tests/tools/test_delegate_tool.py::TestDelegateTaskStringCoercion::test_tasks_as_native_list_still_works PASSED
tests/tools/test_delegate_tool.py::TestDelegateTaskStringCoercion::test_tasks_as_none_with_goal_works PASSED
tests/tools/test_delegate_tool.py::TestDelegateTaskStringCoercion::test_tasks_empty_list_returns_error PASSED
tests/tools/test_delegate_tool.py::TestDelegateTaskStringCoercion::test_tasks_string_with_nested_objects PASSED

Fixes [Bug]: delegate_task batch mode fails silently when model emits tasks as JSON string #21933

liuhao1024 and others added 3 commits April 24, 2026 22:14
…INSTALL_TIMEOUT

Increase the default npm install timeout for WhatsApp bridge from 60s
to 300s (5 minutes) to accommodate slower systems like Unraid NAS.
Make it configurable via WHATSAPP_NPM_INSTALL_TIMEOUT environment variable
for users who need even longer timeouts.

Closes NousResearch#14980
- Add 'path', 'old_string', 'new_string', and 'patch' to required list
- Update description to clarify mode-specific parameter requirements
- This addresses issue where LLMs would omit these parameters because
  they were not marked as required in the schema, even though they
  are required depending on the mode

Fixes NousResearch#15524
Some open-weight models (e.g. Qwen3.6-27B-FP8) emit the 'tasks' array
as a JSON-encoded string instead of a native list.  The generic
coerce_tool_args in model_tools.py handles the common case, but when the
string contains complex nested escaping, json.loads() may fail there.

Add a normalization step at the top of delegate_task() that attempts to
parse a string 'tasks' parameter as JSON before the batch-mode logic.
Returns a clear error if parsing fails.

Fixes NousResearch#21933
@kshitijk4poor

Copy link
Copy Markdown
Collaborator

Closing as superseded by #22436, which fixed #21933 via salvage of #21966 (@Bartok9). Both your PRs targeted the same root cause and reached delegate_task correctly via raw-string normalization; we picked #21966 as the salvage base because it shipped focused tests and structured error returns without scope creep.

This PR mixed in unrelated changes — the WhatsApp npm install timeout in gateway/platforms/whatsapp.py and the PATCH_SCHEMA edits in tools/file_tools.py. The PATCH_SCHEMA change in particular would have been a regression: it marks path, old_string, new_string, and patch all as top-level required, but those parameters are mode-specific (only some are required depending on mode='replace' vs mode='patch'). That's why we couldn't merge as-is.

Thanks for the diagnosis and the upstream issue analysis — happy to take the WhatsApp timeout change as a separate focused PR if you'd like to refile it.

@alt-glitch alt-glitch added type/bug Something isn't working P2 Medium — degraded but workaround exists comp/agent Core agent loop, run_agent.py, prompt builder tool/delegate Subagent delegation duplicate This issue or pull request already exists labels May 9, 2026
@alt-glitch

Copy link
Copy Markdown
Collaborator

Superseded by #22436 which salvaged the fix from #21966 along with diagnostic logging from #22092.

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

Labels

comp/agent Core agent loop, run_agent.py, prompt builder duplicate This issue or pull request already exists P2 Medium — degraded but workaround exists tool/delegate Subagent delegation type/bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants