This issue is automatically created based on existing pull request: #41164: Validate numeric region ownership in AbstractAddress::getRegionId()
Background
The underlying getRegionId() behavior predates the recent Magento security updates.
We started seeing the resulting inconsistent address state block checkout after applying the July 2026 Magento security hardening associated with APSB26-73.
One relevant change in that update moves the validateQuoteAddress plugin from the REST-specific DI scope:
vendor/magento/module-quote/etc/webapi_rest/di.xml
to the global quote DI scope:
vendor/magento/module-quote/etc/di.xml
Previously:
<type name="Magento\Quote\Model\Quote">
<plugin name="validateQuoteAddress" type="Magento\Quote\Plugin\QuoteAddress" />
</type>
was registered only for REST requests.
After the security update, the plugin is registered globally for:
Magento\Quote\Model\Quote
so quote address validation is applied to additional PHP and checkout flows that call setShippingAddress() or setBillingAddress(), not only REST-specific flows.
This security change does not create the invalid region_id, and Magento\Quote\Plugin\QuoteAddress itself is not responsible for validating whether a region belongs to a country.
The inconsistent state can already exist before validation:
country_id = NL
region_id = null
region = "80"
and AbstractAddress::getRegionId() can then materialize the country-mismatched ID:
country_id = NL
region_id = 80
region = "80"
Persisted data observed
We also found already persisted addresses where region_id belonged to a different country than the address itself.
The following query can be used to identify these records in quote addresses:
SELECT
qa.address_id,
qa.quote_id,
qa.address_type,
qa.country_id,
qa.region_id,
qa.region,
dcr.country_id AS region_country_id,
dcr.code,
dcr.default_name,
qa.updated_at
FROM quote_address AS qa
INNER JOIN directory_country_region AS dcr
ON dcr.region_id = qa.region_id
WHERE qa.region_id IS NOT NULL
AND qa.country_id IS NOT NULL
AND qa.country_id <> dcr.country_id
ORDER BY qa.updated_at DESC;
In our database this returned, among others, combinations such as:
address country region_id actual region country
AT 80 DE
NL 80 DE
LU 80 DE
Region ID 80 is:
DE / BAW / Baden-Württemberg
The same inconsistency was also present in saved customer addresses:
SELECT
cae.entity_id,
cae.country_id,
cae.region_id,
cae.region,
dcr.country_id AS region_country_id,
dcr.code,
dcr.default_name,
cae.updated_at
FROM customer_address_entity AS cae
INNER JOIN directory_country_region AS dcr
ON dcr.region_id = cae.region_id
WHERE cae.region_id IS NOT NULL
AND cae.country_id IS NOT NULL
AND cae.country_id <> dcr.country_id
ORDER BY cae.updated_at DESC;
Historical mismatched customer address records were present as far back as 2024 and 2025.
This provides database-level evidence that invalid country/region combinations existed before the July 2026 security hardening.
The mismatch is ultimately rejected later by Magento address validation during quote/order validation.
In our case this became visible as:
Invalid value "80" for field regionId.
The relevant sequence is therefore:
numeric region value
→ AbstractAddress::getRegionId() can materialize a country-mismatched region_id
→ inconsistent address state can be persisted
→ hardened checkout/quote flow reaches address validation
→ country/region mismatch is rejected
→ checkout fails
Therefore the security hardening should be considered the point at which the existing data-integrity issue became visible in our checkout, not the origin of the invalid state.
This issue is automatically created based on existing pull request: #41164: Validate numeric region ownership in AbstractAddress::getRegionId()
Background
The underlying
getRegionId()behavior predates the recent Magento security updates.We started seeing the resulting inconsistent address state block checkout after applying the July 2026 Magento security hardening associated with APSB26-73.
One relevant change in that update moves the
validateQuoteAddressplugin from the REST-specific DI scope:to the global quote DI scope:
Previously:
was registered only for REST requests.
After the security update, the plugin is registered globally for:
so quote address validation is applied to additional PHP and checkout flows that call
setShippingAddress()orsetBillingAddress(), not only REST-specific flows.This security change does not create the invalid
region_id, andMagento\Quote\Plugin\QuoteAddressitself is not responsible for validating whether a region belongs to a country.The inconsistent state can already exist before validation:
and
AbstractAddress::getRegionId()can then materialize the country-mismatched ID:Persisted data observed
We also found already persisted addresses where
region_idbelonged to a different country than the address itself.The following query can be used to identify these records in quote addresses:
In our database this returned, among others, combinations such as:
Region ID
80is:The same inconsistency was also present in saved customer addresses:
Historical mismatched customer address records were present as far back as 2024 and 2025.
This provides database-level evidence that invalid country/region combinations existed before the July 2026 security hardening.
The mismatch is ultimately rejected later by Magento address validation during quote/order validation.
In our case this became visible as:
The relevant sequence is therefore:
Therefore the security hardening should be considered the point at which the existing data-integrity issue became visible in our checkout, not the origin of the invalid state.