`ToolsFunctionOutput::__construct()` leaves `$name` uninitialized when input function is NULL, causing a PHP fatal
>>> [!note] Migrated issue
<!-- Drupal.org comment -->
<!-- Migrated from issue #3583760. -->
Reported by: [ajv009](https://www.drupal.org/user/3653917)
Related to !1472
>>>
<p>[Tracker]<br>
<strong>Update Summary: </strong>One-line fix with regression tests, patch and fork branch ready.<br>
<strong>Short Description: </strong>ToolsFunctionOutput crashes with an uninitialized typed property when constructed with a NULL input function.<br>
<strong>Check-in Date: </strong>04/08/2026<br>
<em>Metadata is used by the <a href="https://www.drupalstarforge.ai/" title="AI Tracker">AI Tracker.</a> Docs and additional fields <a href="https://www.drupalstarforge.ai/ai-dashboard/docs" title="AI Issue Tracker Documentation">here</a>.</em><br>
[/Tracker]</p>
<h3 id="summary-problem-motivation">Problem/Motivation</h3>
<p><code>Drupal\ai\OperationType\Chat\Tools\ToolsFunctionOutput::__construct()</code> only calls <code>setName()</code> when the input is non-NULL, and <code>$name</code> has no default:</p>
<pre><pre>private string $name;<br><br>public function __construct(?ToolsFunctionInputInterface $input = NULL, string $tool_id = '', array $arguments = []) {<br> $this-&gt;setToolId($tool_id);<br> $this-&gt;setInputFunction($input);<br> if ($input !== NULL) {<br> $this-&gt;setName($input-&gt;getName());<br> }<br> $this-&gt;addArguments($arguments);<br>}</pre></pre><p>Constructing with <code>NULL</code> leaves the property uninitialized. Any subsequent <code>getName()</code> throws:</p>
<pre><pre>Error: Typed property Drupal\ai\OperationType\Chat\Tools\ToolsFunctionOutput::$name<br> must not be accessed before initialization</pre></pre><p>This is reachable from <code>OpenAiBasedProviderClientBase::chat()</code> line 360:</p>
<pre><pre>$tools[] = new ToolsFunctionOutput(<br> $input-&gt;getChatTools()-&gt;getFunctionByName($tool['function']['name']),<br> $tool['id'],<br> $arguments<br>);</pre></pre><p>When the LLM returns a tool call for a function name that is not in the current <code>ChatTools</code> input, <code>getFunctionByName()</code> returns NULL and the first argument is NULL. <code>convertToolResponseToObject()</code> then calls <code>getName()</code> on the half-initialized object and kills the entire chat request.</p>
<p>Discovered during review of <a href="https://www.drupal.org/project/ai_agents/issues/3560681">ai_agents#3560681</a>: roughly 28% of multi-loop agent runs with Claude Haiku 4.5 hit this fatal when ai_agents filtered a tool out of its available list and the LLM tried to call it anyway.</p>
<h4 id="summary-steps-reproduce">Steps to reproduce (required for bugs, but not feature requests)</h4>
<p>Minimal unit repro:</p>
<pre><pre>$output = new Drupal\ai\OperationType\Chat\Tools\ToolsFunctionOutput(NULL, 'tool_123', []);<br>$output-&gt;getName(); // Fatal: typed property must not be accessed before initialization</pre></pre><p>Full end-to-end repro (requires ai_agents and an Anthropic-compatible provider):</p>
<ol>
<li>Install ai, ai_agents, ai_provider_anthropic, key. Configure Claude Haiku 4.5 as the default provider for chat_with_tools.</li>
<li>Edit the Taxonomy Agent and set <code>ai_agent:modify_taxonomy_term</code> max_executions to 2 (this requires the ai_agents MR !252 feature for tool ordering constraints).</li>
<li>Run the agent with the task "Add these five taxonomy terms to the Tags vocabulary: Apple, Orange, Pineapple, Grape, Mango."</li>
<li>The LLM sometimes attempts to call <code>modify_taxonomy_term</code> on loop 3 even though ai_agents has filtered it out of the <code>ChatTools</code> input. When it does, the provider's <code>ToolsFunctionOutput</code> constructor is passed a NULL input and the agent crashes.</li>
</ol>
<p>Environment used for the repro:</p>
<ul>
<li>Drupal 11.3.5, PHP 8.3.30</li>
<li>ai 1.3.1, ai_agents 1.3.x-dev (with MR !252 branch), ai_provider_anthropic 1.2.2</li>
<li>Provider: Anthropic, model <code>claude-haiku-4-5</code></li>
<li>Browser/UI: drush scr (no browser involved)</li>
</ul>
<h3 id="summary-proposed-resolution">Proposed resolution</h3>
<p>Default <code>$name</code> to <code>''</code> at the property declaration. One line, backward-compatible, closes the crash path. Patch includes three regression unit tests: baseline (non-NULL input populates name), regression (NULL input no longer crashes on <code>getName()</code>), and <code>setName()</code> after NULL construction.</p>
<pre><pre> src/OperationType/Chat/Tools/ToolsFunctionOutput.php | 9 +++++++-<br> tests/src/Unit/OperationType/Chat/Tools/ToolsFunctionOutputTest.php | 70 ++++++++<br> 2 files changed, 78 insertions(+), 1 deletion(-)</pre></pre><h3 id="summary-remaining-tasks">Remaining tasks</h3>
<ul>
<li>Review and commit the one-line property default.</li>
<li>Separate follow-up: consider whether <code>OpenAiBasedProviderClientBase::chat()</code> should skip the tool call and log when <code>getFunctionByName()</code> returns NULL.</li>
</ul>
<h3>Optional: Other details as applicable (e.g., User interface changes, API changes, Data model changes)</h3>
<p>No UI, API, or data model changes. <code>getName()</code> still returns <code>string</code>; it now returns <code>''</code> instead of throwing in the NULL-input edge case.</p>
<h3 id="summary-ai-usage">AI usage (if applicable)</h3>
<p>[ ] AI Assisted Issue<br>
This issue was generated with AI assistance, but was reviewed and refined by the creator.</p>
<p>[x] AI Assisted Code<br>
This code was mainly generated by a human, with AI autocompleting or parts AI generated, but under full human supervision.</p>
<p>[ ] AI Generated Code<br>
This code was mainly generated by an AI with human guidance, and reviewed, tested, and refined by a human.</p>
<p>[ ] Vibe Coded<br>
This code was generated by an AI and has only been functionally tested.<br>
```</p>
> Related issue: [Issue #3560681](https://www.drupal.org/node/3560681)
> Related issue: [Issue #3582345](https://www.drupal.org/node/3582345)
issue