20.07.2026・TechStuff
20.07.2026・TechStuff

Shopware 6 Hidden Gems #3: sw-expect-packages — let your integration fail fast

Fabian Blechschmidt

Here is a support ticket I have seen in about five different costumes: a middleware pushes orders into Shopware via the Admin API. One day the payloads start failing — or worse, they don’t fail, they just silently write incomplete data. After an hour of digging it turns out someone deactivated a plugin on the shop side, or updated it to a version with a different custom field layout. The API integration had no idea. It just kept talking to a shop that no longer looked like the one it was built for.

While grepping through PlatformRequest.php for another post in this series, I stumbled over a constant that solves exactly this: HEADER_EXPECT_PACKAGES. I had never heard of it. The docs have a page listing special request headers — this one is not on it.

The gem

Send this header with any Admin API request:

curl -X POST https://shop.example/api/_action/sync \
  --header 'sw-expect-packages: swag/paypal:~10.0,acme/erp-connector:^3.2' \
  --data @orders.json

Format: comma-separated package:constraint pairs. The constraint is a normal Composer/Semver constraint (~10.0, ^3.2, >=2.1 <3.0 — whatever Semver understands).

If any expectation is not met, the request is rejected before your controller runs with HTTP 417 Expectation Failed (yes, that status code finally found a job) and error code FRAMEWORK__API_EXPECTATION_FAILED:

{
  "errors": [{
    "status": "417",
    "code": "FRAMEWORK__API_EXPECTATION_FAILED",
    "detail": "Version constraint for swag/paypal is failed. Installed is: 9.5.0"
  }]
}

Your middleware gets a clear, machine-readable „this shop is not what you think it is“ — instead of a mysterious downstream failure three systems later.

What exactly gets checked

The handler is Framework/Api/EventListener/ExpectationSubscriber.php, and it is worth knowing the lookup order (checkPackages(), lines 76-127):

  1. Active plugins first. The subscriber gets the plugin list injected and indexes it by composer name — but only plugins with active = true (getIndexedPackages(), line 140). A plugin that is installed but deactivated counts as missing. That’s exactly what you want: an inactive plugin doesn’t register its services either.
  2. Composer packages as fallback. If the name is not an active plugin, it asks Composer’s InstalledVersions — so you can also pin library dependencies your integration relies on.
  3. Core packages are special-cased. shopware/core, shopware/platform, shopware/storefront, shopware/administration and shopware/elasticsearch all resolve to the shop’s Shopware version. Which means:
sw-expect-packages: shopware/core:~6.7.0

is a one-liner that protects your integration from talking to a shop that got upgraded (or is still waiting for the upgrade you built against).

The check runs on all routes in the api scope, wired into the kernel controller event — you don’t configure anything, it’s always armed.

Where I’d use it

The cost is nothing: one static header on your HTTP client. I added it to our API client defaults the day I found it.

One caveat

The version that gets checked for plugins is the version in the shop’s database (what plugin:list shows), refreshed by plugin:refresh — in practice this matches what’s running, but a shop with a stale plugin list could in theory lie to you. And obviously this is a convenience contract, not a security feature: it protects you from accidents, not from a malicious shop.

TL;DR

Add sw-expect-packages: vendor/plugin:^1.2,shopware/core:~6.7.0 to your Admin API requests. Shopware validates the constraints against active plugins and Composer packages before your request is processed, and answers with 417 Expectation Failed if the shop doesn’t match. Fail fast beats debug slow. Not documented anywhere — not even on the request headers page that documents its sibling headers 😀

Next up: the feature flag system — which can do way more than FEATURE_ALL=1, including toggling flags in production via REST API.

Questions, or found a header I missed? Drop me an email!


Found in Shopware 6.7.0.0: PlatformRequest.php (HEADER_EXPECT_PACKAGES = 'sw-expect-packages'), check logic in Framework/Api/EventListener/ExpectationSubscriber.php:51-148, 417 response in Framework/Api/Exception/ExpectationFailedException.php:35.