← Alle Beiträge

Shopware 6: „Invalid ids provided in criteria. Ids should not be empty.“ finden und beheben

Shopware 6: „Invalid ids provided in criteria. Ids should not be empty.“ finden und beheben

Ein Kunde ruft eine Seite auf und bekommt eine Fehlerseite. Im Log steht eine DataAbstractionLayerException, die nichts über den Shop verrät, nur dass irgendwo eine Liste leer war. Bei einem Shop, den wir überwachen, kam das über 60 Mal in wenigen Tagen, immer aus Storefront-Requests. Der Stack-Frame im Logeintrag zeigte nur auf Shopware selbst.

Der Frame lügt nicht, aber er zeigt auf die falsche Stelle. Shopware wirft den Fehler, verursacht hat ihn fast immer Code, der Shopware aufruft.

Die Fehlermeldung

request.ERROR: Uncaught PHP Exception
Shopware\Core\Framework\DataAbstractionLayer\DataAbstractionLayerException:
"Invalid ids provided in criteria. Ids should not be empty. Ids: Array ( ) ."
at DataAbstractionLayerException.php line 187

Je nach Version steht dort auch InvalidCriteriaIdsException als Klasse. Die Zeilennummer ändert sich mit der Shopware-Version und ist für die Fehlersuche egal.

Was dahintersteckt

Eine Criteria ist in Shopware die Suchanfrage an ein Repository. Man kann ihr im Konstruktor direkt eine Liste von IDs mitgeben:

$criteria = new Criteria([$productId]);

Diese Liste darf nicht leer sein. In 6.4 (zuletzt in 6.4.20.0 nachgesehen) gab es dafür nur eine Deprecation, ab 6.5.0.0 wirft der Konstruktor einen Fehler. Dort war es noch eine schlichte RuntimeException mit dem Text Empty ids provided in criteria. Spätestens ab 6.5.5.0 kommt stattdessen die DataAbstractionLayerException mit der Meldung von oben, und die Prüfung ist bis heute drin.

Überraschend für viele: Der Konstruktor filtert die Liste vorher mit array_filter(). Leere Strings und null fallen raus. Nicht nur new Criteria([]) knallt also, sondern auch

new Criteria([null]);
new Criteria(['']);
new Criteria([$customer->getDefaultBillingAddressId()]); // wenn die ID mal null ist

Und genau so sieht der Fehler in echten Shops meistens aus. Kein Entwickler schreibt absichtlich eine leere Liste hinein. Es ist eine ID, die in 99 % der Fälle gesetzt ist und im hundertsten Fall nicht: eine fehlende Plugin-Konfiguration, ein Produkt ohne Hersteller, ein Kunde ohne Standardadresse, eine Kategorie, die gelöscht wurde, während irgendwo noch eine Referenz darauf gespeichert ist.

In aktuellen Versionen läuft auch $criteria->setIds([]) durch dieselbe Prüfung. (In 6.5.5.0 ging das noch stillschweigend durch.)

Weil die Exception als HTTP 500 beim Kunden ankommt, sieht er eine Fehlerseite, auch wenn es nur um ein Widget in der Sidebar ging.

Die Stelle finden

Der Eintrag oben ist gekürzt. Der vollständige Stack-Trace steht in var/log/ im Logfile der Umgebung, bei Standard-Setups var/log/prod-JJJJ-MM-TT.log. Such nach der Meldung und lies den Trace von oben nach unten, bis zum ersten Frame, der nicht unter vendor/shopware/ liegt:

grep -n "Ids should not be empty" var/log/prod-*.log | tail -5

Der erste Frame aus custom/plugins/, custom/static-plugins/ oder vendor/<hersteller>/ ist in den allermeisten Fällen der Verursacher. Dort steht ein new Criteria([...]) oder ein setIds(...) mit einer Variable, die leer sein kann.

Ist der Trace abgeschnitten oder fehlt er ganz, hilft nur Eingrenzen:

  • Welche URL hat den Fehler ausgelöst? Das Request-Log oder das Access-Log des Webservers verrät, ob es immer dieselbe Produkt- oder Kategorieseite ist.
  • Läuft der Fehler seit einem bestimmten Datum? Dann das Plugin-Update oder den Import von diesem Tag ansehen.
  • Auf einer Kopie des Shops Plugins einzeln deaktivieren, bis die betroffene URL durchläuft. Mühsam, aber zuverlässig.

Im eigenen Code kannst du auch direkt suchen:

grep -rn "new Criteria(\[" custom/plugins/ custom/static-plugins/ | grep -v "new Criteria(\[\])"

Jede Fundstelle, an der eine Variable in die Liste geht, ist ein Kandidat.

Die Lösung

Im eigenen Code ist der Fix klein. Vorher prüfen, ob es überhaupt etwas zu laden gibt:

$ids = array_filter([$manufacturerId]);

if ($ids === []) {
    return null; // oder eine leere Collection, je nachdem, was der Aufrufer erwartet
}

$manufacturer = $this->manufacturerRepository
    ->search(new Criteria($ids), $context)
    ->first();

Und wo jemand eine Criteria ohne ID-Einschränkung wollte und new Criteria([]) geschrieben hat, um „alle“ zu bekommen: den Parameter einfach weglassen.

$criteria = new Criteria();
$criteria->addFilter(new EqualsFilter('active', true));

Steckt der Fehler in einem gekauften Plugin, ist der richtige Weg ein Ticket beim Hersteller, mit Stack-Trace und auslösender URL. Bis zum Fix kannst du nur an den Daten ansetzen, denn die leere ID kommt ja irgendwoher. Fehlt bei einem Produkt der Hersteller oder bei einem Kunden die Standardadresse, ist das Nachpflegen oft der schnellste Workaround. Ein Fix ist es nicht: Das Plugin bricht beim nächsten Datensatz mit derselben Lücke wieder.

Wenn das nicht hilft

Taucht der Fehler nach einem Shopware-Update auf, ohne dass sich an den Plugins etwas geändert hat, kann auch Core-Code betroffen sein. Für bin/console media:delete-unused gibt es zum Beispiel einen Bericht unter Shopware 6.6.7.0 mit genau dieser Meldung (shopware/shopware#5298). Unter 6.5.x lief der Befehl noch. Laut einem Kommentar im Issue ist das Problem inzwischen behoben und intern als NEXT-39281 verlinkt, in welcher Version, steht dort nicht. Bei einem ähnlichen Fall im Core hilft ein Issue bei Shopware, mit Stack-Trace, Version und auslösendem Befehl.

Die andere Meldung aus derselben Familie, Ids should be a list of strings or a list of key value pairs, hat eine andere Ursache: Dort ist die Liste nicht leer, sondern enthält etwas, das keine ID ist, oft einen Integer oder ein ganzes Entity-Objekt statt seiner ID.

Dieser Fehler fiel erst auf, weil jemand die Logs gelesen hat. Wer das nicht jeden Morgen tun will: ShopSignal überwacht Shopware-6-Shops und meldet, wenn etwas nicht mehr so läuft, wie es soll.