Repository navigation
fix(executor): handle error subtypes in ResultMessage to prevent task… - #476
Conversation
… stuck When Claude SDK returns ResultMessage with subtype like "error_during_execution" but is_error=False, the task would remain in RUNNING state indefinitely. This fix handles all error_ prefixed subtypes per official documentation: - error_during_execution: Error occurred during execution - error_max_turns: Reached turn limit - error_max_budget_usd: Exceeded budget limit - error_max_structured_output_retries: Reached structured output retry limit Ref: https://platform.claude.com/docs/agent-sdk/stop-reasons Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
📝 WalkthroughWalkthroughThe PR adds explicit handling for error subtypes in ResultMessage processing within the response processor. When a message subtype starts with "error_", it logs the subtype, adds a thinking step, updates the workbench status to failed, reports progress failure, and returns TaskStatus.FAILED, ensuring error conditions are treated as hard failures. Changes
Estimated code review effort🎯 2 (Simple) | ⏱️ ~10 minutes Possibly related PRs
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 3✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing touches
🧪 Generate unit tests (beta)
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. Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Fix all issues with AI agents
In `@executor/agents/claude_code/response_processor.py`:
- Around line 746-776: Guard uses of state_manager in the error-subtype handling
branch (the block that checks if msg.subtype and
msg.subtype.startswith("error_") and returns TaskStatus.FAILED) by checking if
state_manager is not None before calling state_manager.update_workbench_status
and state_manager.report_progress; if state_manager is None skip those calls
(but still log and add the thinking step via thinking_manager as currently
done). Apply the same None-guard pattern to the earlier is_error handling branch
(the block that handles msg.is_error) to prevent AttributeError when
state_manager is None. Ensure you reference the same symbols: msg.subtype,
thinking_manager.add_thinking_step, state_manager.update_workbench_status,
state_manager.report_progress, and TaskStatus.FAILED.
🧹 Nitpick comments (1)
executor/agents/claude_code/response_processor.py (1)
752-776: Consider extracting the shared failure-reporting logic to reduce duplication.Lines 760–776 are nearly identical to lines 726–744 (the
is_errorfailure path): add thinking step → update workbench to "failed" → report progress at 100% FAILED → returnTaskStatus.FAILED. Extracting a small helper (e.g.,_report_failure(state_manager, thinking_manager, masked_details, message)) would eliminate this duplication and make future changes (like theNoneguard above) a single-point fix.
| # Handle error subtypes (is_error=False but execution ended with error condition) | ||
| # Official subtypes per https://platform.claude.com/docs/agent-sdk/stop-reasons: | ||
| # - error_during_execution: Error occurred during execution | ||
| # - error_max_turns: Reached turn limit | ||
| # - error_max_budget_usd: Exceeded budget limit | ||
| # - error_max_structured_output_retries: Reached structured output retry limit | ||
| if msg.subtype and msg.subtype.startswith("error_"): | ||
| result_str = ( | ||
| str(msg.result) if msg.result is not None else f"Task ended: {msg.subtype}" | ||
| ) | ||
| logger.error( | ||
| f"Task ended with subtype={msg.subtype} (is_error={msg.is_error}): {result_str}" | ||
| ) | ||
|
|
||
| # Add thinking step for error result | ||
| if thinking_manager: | ||
| thinking_manager.add_thinking_step( | ||
| title="thinking.task_execution_failed", | ||
| report_immediately=False, | ||
| use_i18n_keys=True, | ||
| details=masked_result_details, | ||
| ) | ||
|
|
||
| # Update workbench status to failed | ||
| state_manager.update_workbench_status("failed") | ||
|
|
||
| # Report error using state manager | ||
| state_manager.report_progress( | ||
| progress=100, status=TaskStatus.FAILED.value, message=result_str | ||
| ) | ||
| return TaskStatus.FAILED |
There was a problem hiding this comment.
Logic is correct and well-documented; guard state_manager against None for robustness.
The placement and condition (msg.subtype.startswith("error_") after the is_error gate) correctly catch the described scenario where is_error=False but the subtype signals an error. The startswith approach is nicely forward-compatible with future error subtypes.
However, state_manager is used at lines 770 and 773 without a None check. The outer exception handler (line 181) and other places in process_response (line 91) do guard against None. While this mirrors the existing is_error block (line 736), both paths can raise AttributeError if state_manager is None.
Proposed fix
# Update workbench status to failed
- state_manager.update_workbench_status("failed")
+ if state_manager:
+ state_manager.update_workbench_status("failed")
# Report error using state manager
- state_manager.report_progress(
- progress=100, status=TaskStatus.FAILED.value, message=result_str
- )
+ state_manager.report_progress(
+ progress=100, status=TaskStatus.FAILED.value, message=result_str
+ )
return TaskStatus.FAILEDThe same fix should be applied to the pre-existing is_error block at lines 736–741.
🤖 Prompt for AI Agents
In `@executor/agents/claude_code/response_processor.py` around lines 746 - 776,
Guard uses of state_manager in the error-subtype handling branch (the block that
checks if msg.subtype and msg.subtype.startswith("error_") and returns
TaskStatus.FAILED) by checking if state_manager is not None before calling
state_manager.update_workbench_status and state_manager.report_progress; if
state_manager is None skip those calls (but still log and add the thinking step
via thinking_manager as currently done). Apply the same None-guard pattern to
the earlier is_error handling branch (the block that handles msg.is_error) to
prevent AttributeError when state_manager is None. Ensure you reference the same
symbols: msg.subtype, thinking_manager.add_thinking_step,
state_manager.update_workbench_status, state_manager.report_progress, and
TaskStatus.FAILED.
… stuck (wecode-ai#476) When Claude SDK returns ResultMessage with subtype like "error_during_execution" but is_error=False, the task would remain in RUNNING state indefinitely. This fix handles all error_ prefixed subtypes per official documentation: - error_during_execution: Error occurred during execution - error_max_turns: Reached turn limit - error_max_budget_usd: Exceeded budget limit - error_max_structured_output_retries: Reached structured output retry limit Ref: https://platform.claude.com/docs/agent-sdk/stop-reasons Co-authored-by: yunpeng7 <yunpeng7@staff.weibo.com> Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
feat: Add OpenAPI v1/responses endpoint and API Key management (#476) See merge request weibo_rd/common/wecode/wegent!766
… stuck
When Claude SDK returns ResultMessage with subtype like "error_during_execution" but is_error=False, the task would remain in RUNNING state indefinitely.
This fix handles all error_ prefixed subtypes per official documentation:
Ref: https://platform.claude.com/docs/agent-sdk/stop-reasons
Summary by CodeRabbit