Environment
- Checkout UI extension, target
purchase.checkout.delivery-address.render-after - Reproduced on
api_version = "2025-07"(React) andapi_version = "2026-07"(Preact + Polaris web components,@shopify/ui-extensions2026.7.1,preact10.29.8,@preact/signals2.11.0) - Shopify Plus development store, Brazilian delivery address form, native address autocomplete enabled
What we’re building
A postal code (CEP) validator: when the buyer enters a CEP, we fetch the official street from our postal code service and keep the delivery address consistent — correcting street, city and province when they diverge.
City and province work perfectly. The street never applies.
The core evidence
A single call, one payload, two fields:
await shopify.applyShippingAddressChange({
type: "updateShippingAddress",
address: {
provinceCode: "SC",
address1: "Rodovia Haroldo Soares Glavan",
},
});
// → {type: "success", errors: null}
//
// provinceCode: applied
// address1: unchanged ("Estrada Haroldo Soares Glavan 31231")
Same call, same instant, same payload object. provinceCode is written, address1 is silently discarded, and the result reports success with errors: null.
Minimal repro
- BR delivery address form, complete and valid address (first/last name, CEP, street, number, city, province).
- From an extension on
purchase.checkout.delivery-address.render-after, apply a change that includesaddress1and any other address field. - The other field is applied;
address1is not. Result issuccess.
What we ruled out
- Value format. Tried
"Street","Street, 123","Street123"and"Street, 123"(with U+2060 WORD JOINER). We compared code points on both sides and sent byte-identical formatting to what the checkout itself produces. Ignored in every case. - Payload shape.
{address1},{address1, zip},{address1, address2, zip, city, provinceCode}. Ignored. - Missing subfield. We hypothesized the checkout might reject an
address1it can’t split into street and number. Tried both a street-only value (leaving the number empty) and a well-formedstreet + ", " + U+2060 + number. Both ignored. - Address validity. Tried with all required fields filled and with the number missing. Ignored either way. Note that a full payload including names on an incomplete address does return a real error (
{"message":"Enter a last name","field":"lastName"}), so the mutation validates and can report errors — it just never reports anything aboutaddress1. - Concurrency. Serialized to a single in-flight write.
- Rate limiting. Ruled out by the two-field call above, which applied
provinceCodein a fresh session. - API version. Same behavior on 2025-07 and 2026-07.
Our current theory
In the BR checkout, address1 looks like a derived value, not a field. Inspecting the delivery form inputs:
streetName = "Rodovia Haroldo Soares Glavan" ← real input
streetNumber = "767" ← real input
district / line2 / city / zone / postalCode ← real inputs
address1 = "Rodovia Haroldo Soares Glavan, 767" ← hidden, composed from the two
The checkout appears to recompose address1 from streetName + streetNumber and discard external writes. city and provinceCode work because they map 1:1 to real inputs. Meanwhile streetName / streetNumber are not exposed by the Checkout UI Extensions API, and they don’t exist in the LocalizedFieldKey union (which only has TAX_CREDENTIAL_* and SHIPPING_CREDENTIAL_*).
Questions
- Is writing
address1supported in locales that use the split street/number address form (BR and similar)? If not, is this documented? - If it isn’t supported, could
applyShippingAddressChangereturn a field-level error for the ignored field instead ofsuccess? A silent no-op is extremely expensive to diagnose. - Is there any supported way to set the street programmatically in these locales — an API for
streetName/streetNumber— or ispurchase.address-autocomplete.suggestthe only intended path?
Three side notes that may save others time
- The separator inside
address1is invisible. BR uses", "plus a WORD JOINER (U+2060) between street and number, andJSON.stringifydoes not escape U+2060 — so it’s invisible in any log inspected that way. We reached two wrong conclusions before printing code points explicitly. - Rate limiting is indistinguishable from this bug. Per the docs, an extension that makes too many changes “can’t make further changes during the buyer’s session”. While rate limited, calls still resolve with
type: "success"and do nothing. TheTooManyChangesErroronly surfaced as an uncaught exception inside our async handler. Reflecting that state in the result — or in a readable API property — would help a lot. - Migrating to Preact needs an explicit JSX pragma. The docs’ migration example shows
import '@shopify/ui-extensions/preact'; import {render} from 'preact';with no pragma. Without/** @jsxImportSource preact */in the file, our build produced vnodes from a different JSX runtime and mounting failed withTypeError: Cannot add property __, object is not extensible. Worth adding to the migration guide.