Undefined array key "#type" warnings break AJAX in AbstractModelFormBase::updateModelForm()
>>> [!note] Migrated issue
<!-- Drupal.org comment -->
<!-- Migrated from issue #3585328. -->
Reported by: [mtift](https://www.drupal.org/user/751908)
Related to !1519
>>>
<p>[Tracker]<br>
<strong>Update Summary: </strong>New bug report — one-line patch ready<br>
<strong>Short Description: </strong>Undefined array key "#type" warnings break AJAX in model form<br>
<strong>Check-in Date: </strong>04/23/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>AbstractModelFormBase::updateModelForm()</code> iterates every key under <code>$form['model_data']</code> and assumes each child has a <code>#type</code> key:</p>
<div class="codeblock">
<pre><span style="color: #000000"><span style="color: #0000BB"><?php<br></span><span style="color: #007700">foreach (</span><span style="color: #0000BB">$form</span><span style="color: #007700">[</span><span style="color: #DD0000">'model_data'</span><span style="color: #007700">] as </span><span style="color: #0000BB">$key </span><span style="color: #007700">=> </span><span style="color: #0000BB">$element</span><span style="color: #007700">) {<br> if (</span><span style="color: #0000BB">is_array</span><span style="color: #007700">(</span><span style="color: #0000BB">$element</span><span style="color: #007700">) && </span><span style="color: #0000BB">$element</span><span style="color: #007700">[</span><span style="color: #DD0000">'#type'</span><span style="color: #007700">] === </span><span style="color: #DD0000">'checkbox'</span><span style="color: #007700">) {<br> </span><span style="color: #0000BB">$form</span><span style="color: #007700">[</span><span style="color: #DD0000">'model_data'</span><span style="color: #007700">][</span><span style="color: #0000BB">$key</span><span style="color: #007700">][</span><span style="color: #DD0000">'#checked'</span><span style="color: #007700">] = </span><span style="color: #0000BB">FALSE</span><span style="color: #007700">;<br> }<br>}<br></span><span style="color: #0000BB">?></span></span></pre></div>
<p>Render-array metadata keys (<code>#tree</code>, <code>#process</code>, etc.) are arrays without <code>#type</code>, so PHP emits <code>Undefined array key "#type"</code> warnings. Because this code runs inside an AJAX callback, the warning output is injected into the JSON response and the browser fails to parse it, producing:</p>
<div class="codeblock">
<pre><span style="color: #000000"><span style="color: #0000BB"><?php<br>StatusText</span><span style="color: #007700">: </span><span style="color: #0000BB">parsererror<br>ResponseText</span><span style="color: #007700">: </span><span style="color: #0000BB">Warning</span><span style="color: #007700">: </span><span style="color: #0000BB">Undefined </span><span style="color: #007700">array </span><span style="color: #0000BB">key </span><span style="color: #DD0000">"#type" </span><span style="color: #0000BB">in </span><span style="color: #007700">.../</span><span style="color: #0000BB">AbstractModelFormBase</span><span style="color: #007700">.</span><span style="color: #0000BB">php on line 112<br>?></span></span></pre></div>
<p>The user-visible result is a generic <em>"An AJAX HTTP error occurred. HTTP Result Code: 200"</em> dialog, with no model created.</p>
<h4 id="summary-steps-reproduce">Steps to reproduce (required for bugs, but not feature requests)</h4>
<p>Environment:</p>
<ul>
<li>Drupal core 10.x</li>
<li><code>drupal/ai</code> 1.3.3</li>
<li><code>ai_provider_google_vertex</code> 1.1.1</li>
<li><code>key</code> module enabled with a Google service account key configured</li>
<li>Browser: Firefox</li>
</ul>
<p>Steps:</p>
<ol>
<li>Navigate to <code>/admin/config/ai/providers/google_vertex</code>.</li>
<li>Click <strong>Add Chat Model</strong>.</li>
<li>Submit the form (or trigger any AJAX rebuild on it).</li>
<li>Observe the JS error dialog and the warnings in the AJAX response payload.</li>
</ol>
<h3 id="summary-proposed-resolution">Proposed resolution</h3>
<p>Guard the <code>#type</code> access with <code>isset()</code>:</p>
<div class="codeblock">
<pre><span style="color: #000000"><span style="color: #0000BB"><?php<br></span><span style="color: #007700">foreach (</span><span style="color: #0000BB">$form</span><span style="color: #007700">[</span><span style="color: #DD0000">'model_data'</span><span style="color: #007700">] as </span><span style="color: #0000BB">$key </span><span style="color: #007700">=> </span><span style="color: #0000BB">$element</span><span style="color: #007700">) {<br> if (</span><span style="color: #0000BB">is_array</span><span style="color: #007700">(</span><span style="color: #0000BB">$element</span><span style="color: #007700">) && isset(</span><span style="color: #0000BB">$element</span><span style="color: #007700">[</span><span style="color: #DD0000">'#type'</span><span style="color: #007700">]) && </span><span style="color: #0000BB">$element</span><span style="color: #007700">[</span><span style="color: #DD0000">'#type'</span><span style="color: #007700">] === </span><span style="color: #DD0000">'checkbox'</span><span style="color: #007700">) {<br> </span><span style="color: #0000BB">$form</span><span style="color: #007700">[</span><span style="color: #DD0000">'model_data'</span><span style="color: #007700">][</span><span style="color: #0000BB">$key</span><span style="color: #007700">][</span><span style="color: #DD0000">'#checked'</span><span style="color: #007700">] = </span><span style="color: #0000BB">FALSE</span><span style="color: #007700">;<br> }<br>}<br></span><span style="color: #0000BB">?></span></span></pre></div>
<p>A more idiomatic fix would be to iterate <code>\Drupal\Core\Render\Element::children($form['model_data'])</code> instead, which skips render-array properties by definition. Either approach resolves the warnings.</p>
<h3 id="summary-remaining-tasks">Remaining tasks</h3>
<ul>
<li>Decide between <code>isset()</code> guard vs. <code>Element::children()</code> iteration</li>
<li>Patch + test</li>
<li>Reviews</li>
</ul>
<h3>Related issues</h3>
<ul>
<li><span class="drupalorg-gitlab-issue-link project-issue-status-info project-issue-status-14"><a href="https://www.drupal.org/project/ai/issues/3584884" title="Status: Reviewed & tested by the community">#3584884: Undefined array key warnings in RestrictToTopic and RegexpGuardrail buildConfigurationForm() when creating new guardrail</a></span> — same class of bug (unsafe array access in form code), different files and different fix (<code>?:</code> vs <code>??</code>). Worth a broader audit of form-handling code in the module.</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. The fix is internal to one method.</p>
<h3 id="summary-ai-usage">AI usage (if applicable)</h3>
<p>[x] AI Assisted Issue<br><br>
This issue was generated with AI assistance, but was reviewed and refined by the creator.</p>
<p>[ ] AI Assisted Code<br><br>
[ ] AI Generated Code<br><br>
[ ] Vibe Coded</p>
issue