Problem/Motivation
Exposing a filter through the ai_agents_views:update_view_handler_option tool without explicitly supplying an expose.identifier saved the filter with an empty identifier - config that Drupal core's own View::validate() rejects outright ("The identifier is required if the filter is exposed."), and it has no usable query parameter even if validation were skipped, so a visitor can never actually submit the exposed filter.
An LLM asked to "expose the status filter" naturally supplies exposed: true and often a label, but has no particular reason to also invent an identifier unless told to - yet the identifier is exactly the field that makes an exposed filter usable at all.
Steps to reproduce
- Add a filter handler to a View.
- Call
ai_agents_views:update_view_handler_optionwithoptions:{"exposed":true,"expose":{"label":"Active"}}- noidentifierkey. - Load the View and inspect the filter's saved
expose.identifier, or run$view->validate().
Expected: the identifier defaults to something usable (the field ID), and the View validates cleanly.
Actual (before this fix): expose.identifier is saved as an empty string, and validate() reports "The identifier is required if the filter is exposed."
Root cause
execute() merged the LLM-supplied options with the handler's existing configuration and saved the result directly, with no post-processing:
$view_executable->setHandler($display_id, $handler_type, $field_id, $options + $configuration);If $options sets exposed: true and an expose array without an identifier key, the merged result simply has no identifier. Drupal core applies no default here on save; the identifier is only auto-filled by FilterPluginBase::defaultExposeOptions() when a handler is first exposed through the Views UI form, a code path this tool never goes through.
Proposed resolution
After merging, default the identifier to the handler's own field ID whenever the filter is exposed and no identifier was supplied - matching Drupal core's own default for a newly exposed handler:
$final_options = $options + $configuration; if (($final_options['exposed'] ?? FALSE) && ($final_options['expose']['identifier'] ?? '') === '') { // An exposed handler with no identifier has no usable query // parameter and fails View validation, so default it to the field ID. $final_options['expose']['identifier'] = $field_id; } $view_executable->setHandler($display_id, $handler_type, $field_id, $final_options);
An explicitly supplied identifier is left untouched - this only fills the gap when the LLM omitted it entirely.
Remaining tasks
- File this issue on drupal.org.
- Open an MR with the fix above.
Note: A follow-up issue was found that this same identifier default, combined with a separate shallow-merge behaviour in this method, could silently overwrite an already-set custom identifier during a later partial edit - filed and fixed separately, since it's a distinct defect with its own root cause.
Test coverage added locally
Regression test testExposingFilterWithoutIdentifierDefaultsToFieldId() in tests/src/Kernel/Plugin/AiFunctionCall/UpdateViewHandlerOptionsTest.php exposes a filter with a label but no identifier, and asserts both that the saved identifier equals the field ID and that View::validate() no longer reports the missing-identifier error.
Full suite with this fix applied (isolated on its own branch): 10/10 kernel tests passing (79 assertions) in UpdateViewHandlerOptionsTest, no failures.
API changes
None. This is a bug fix to existing, already-broken behaviour - no new context parameters, no signature changes, no config schema changes.
Issue fork ai_agents_views-3621943
Show commands
Start within a Git clone of the project using the version control instructions.
Or, if you do not have SSH keys set up on git.drupalcode.org:
Comments
Comment #3
jibranComment #5
jibranCommitted and pushed to 1.0.x