I’m seeing inconsistent scope enforcement on the inventoryAdjustQuantities mutation. My app’s declared scopes in shopify.app.toml do not include write_inventory, only read_inventory. I’ve confirmed the stored offline access token’s scope field is:
read_all_orders,read_fulfillments,read_inventory,read_locations,read_merchant_managed_fulfillment_orders,read_orders,read_reports,read_third_party_fulfillment_orders,write_products
No write_inventory present.
What happened:
My app called inventoryAdjustQuantities from a Cloudflare Worker using the offline access token (obtained via token exchange with requested_token_type: offline-access-token). The mutation succeeded and Shopify’s adjustment history shows the change with the correct referenceDocumentUri.
A few hours later, the same mutation with the same offline access token was called again (different payload, same token). This time it failed with:
{
"message": "Access denied for inventoryAdjustQuantities field. Required access: `write_inventory` access scope.",
"extensions": { "code": "ACCESS_DENIED" }
}
A background retry task using the same stored offline token also failed 3 times with the identical ACCESS_DENIED error.
Expected behavior:
Both calls should have been denied, since the token doesn’t have write_inventory.
Actual behavior:
The first call succeeded, the second was denied. The scope enforcement appears inconsistent.
Environment:
- API version: 2026-01
- Auth flow: Managed install + token exchange (offline access token, expiring)
- The app is embedded, using App Bridge v4
Is there a known issue with scope enforcement on inventoryAdjustQuantities, or can offline tokens sometimes inherit broader permissions than their declared scopes?