bug: #3586653 AiToolsValidationException is not caught and passed back as tool output
Description
- Adds AiToolsRecoverableExceptionInterface (extends AiExceptionInterface) as the contract for tool failures that belong back in the conversation rather than in front of the user.
- Makes AiFunctionCallingExecutionError implement that marker.
- Makes AiToolsValidationException extend AiFunctionCallingExecutionError.
Why re-parent rather than only add the interface
- PHP matches catch on class inheritance or implemented interfaces only, and the released ai_agents catch names AiFunctionCallingExecutionError concretely. An interface alone, or a shared abstract base class, would fix nothing until ai_agents also changed and both projects released in lockstep.
- Re-parenting makes the reproduced case work today against ai_agents 1.3.x with no change there, while the interface gives ai_agents and third-party runners a forward path to catch (AiToolsRecoverableExceptionInterface) and pick up every module's recoverable tool errors at once.
- As the issue notes, catching AiExceptionInterface wholesale would have swallowed AiRateLimitException and AiRequestErrorException. Those stay outside the marker and keep failing loudly; the test pins that.
BC note
- This widens what an existing catch (AiFunctionCallingExecutionError) block receives, by design, and that is the fix. Code ordering that catch before catch (AiToolsValidationException) would now hit the first block. No such ordering exists in ai or ai_agents, and AiFunctionCallingExecutionError has exactly one consumer in contrib (AiAgentEntityWrapper.php:520). Nothing is narrowed, so no existing catch stops working.
Testing instructions
- Install ai and ai_agents, enable ai_agents_explorer, and configure a provider that supports chat_with_tools.
- Go to /admin/config/ai/agents/explore, select Field Agent (field_agent_triage), and prompt: Show me the form display settings for the field_tags field on the page content type. Any field name that does not exist on the bundle will do.
- Before this MR: the run aborts with a red panel reading "Error: The field field_tags does not exist on the entity type node."
- After this MR: no error panel. The validation message lands in the chat history as a tool-role message, the agent loops once more, and answers along the lines of "field_tags does not exist on the node entity type, so there are no form display settings for it on the page content type."
Checklist
- I have linked the related issue in the MR title or description
- I have performed a self-review of my own code
- I have added or updated tests, or explained in the description why this change is not covered by tests
- I have updated documentation for any new or changed functionality
- I have written testing instructions and verified them locally
- I have noted any required post-merge steps (config imports, cache rebuilds, manual changes)
- This MR contains no breaking API or hook changes, or they are explicitly documented in the description
AI Compliance
Note
Check the one that best describes your usage, or leave all unchecked if AI was not significantly used.
-
AI Assisted Code
Mainly written by a human; AI used for autocomplete or partial generation under full human supervision. -
AI Generated Code
Mainly generated by AI, reviewed and approved by a human before this MR was created. -
Vibe Coded
Generated by AI and only functionally reviewed before this MR was created.
Closes #3586653