QueryBuilder indicators throw "No query builder block found" when an applied rule is removed in the deferred filters form
Package
filament/tables
Package Version
v5.8.2 (also v5.8.1)
Laravel Version
v13.32.0
Livewire Version
v4.4.5
PHP Version
PHP 8.4.23
Problem description
With deferred filters (the default), a QueryBuilder filter throws a LogicException during a full render when a rule is applied but has since been deleted from the filters form without applying again:
No query builder block found for [price]. (View: vendor/filament/tables/resources/views/index.blade.php)
at vendor/filament/tables/src/Filters/QueryBuilder.php:100QueryBuilder::getRuleSummaries() (called from indicateUsing()) receives the rules from the applied state (tableFilters, via getTableFilterState()), but looks each rule up with $ruleBuilder->getChildSchema($ruleIndex). The RuleBuilder lives in the filters form, whose state path is tableDeferredFilters, so its child schemas are built from the deferred state. When the two differ structurally (rule deleted in the form, not applied yet), the lookup returns null and the exception is thrown.
applyFiltersToTableQuery() already handles this mismatch by temporarily switching the form state path to tableFilters; building the indicators does not.
It only surfaces on a full render. The builder's delete action renders partially, so clicking delete on its own is fine. But when the same request also carries a pending deferred wire:model update (e.g. the user changed another field in the filters form first), PartialsComponentHook falls back to a full render, the table view calls getFilterIndicators(), and the request crashes. In production the logged Livewire method was callMountedAction.
Expected behavior
The table keeps rendering. The indicators should be built from the applied state with a rule builder that matches that state (or rules without a matching block should be skipped) instead of throwing.
Steps to reproduce
- A resource table with deferred filters, a
SelectFilterand aQueryBuilder:
->filters([
SelectFilter::make('status')
->options([
'active' => 'Active',
'archived' => 'Archived',
]),
QueryBuilder::make('extra')
->constraints([
NumberConstraint::make('price'),
]),
])- Open the filters, add a rule "Price is minimum 10" and apply.
- Open the filters again, change the Status select (don't apply), then click delete on the price rule.
- The request fails with
No query builder block found for [price].
The reproduction repository contains a failing test for this (tests/Feature/QueryBuilderIndicatorsTest.php):
$rule = ['type' => 'price', 'data' => ['operator' => 'isMin', 'settings' => ['number' => 10]]];
$component = Livewire::test(ListProducts::class)
->set('tableDeferredFilters.extra.rules', ['rule-uuid' => $rule])
->call('applyTableFilters')
->mountAction(TestAction::make('delete')
->schemaComponent('extra.rules', schema: 'tableFiltersForm')
->arguments(['item' => 'rule-uuid']));
// A pending deferred update in the same request forces a full render, as in the browser
$component->update(
calls: [['method' => 'callMountedAction', 'params' => [], 'path' => '']],
updates: ['tableDeferredFilters.status.value' => 'archived'],
);Workaround we use for now, a subclass that skips rules the builder doesn't know:
public function getRuleSummaries(array $rules, RuleBuilder $ruleBuilder, int $iteration = 1): array
{
$knownRules = array_filter(
$rules,
fn (mixed $rule, int|string $ruleIndex): bool => $ruleBuilder->getChildSchema($ruleIndex) !== null,
ARRAY_FILTER_USE_BOTH,
);
return parent::getRuleSummaries($knownRules, $ruleBuilder, $iteration);
}Reproduction repository (issue will be closed if this is not valid)
https://github.com/MaartenJC/filament-querybuilder-repro
Run php artisan test --filter=QueryBuilderIndicatorsTest.
Relevant log output
Illuminate\View\ViewException: No query builder block found for [price]. (View: vendor/filament/tables/resources/views/index.blade.php) (View: vendor/filament/tables/resources/views/index.blade.php)
at vendor/filament/tables/src/Filters/QueryBuilder.php:100Source: filamentphp/filament