Invalid variant ID

Topic summary

A developer using Shopify’s Mobile Buy SDK for iOS encounters a “Variant is invalid” error (later “Variant product is not published for this customer”) when USA-based customers attempt checkout, while other regions (Australia, UK, EU) work fine.

Verified configurations:

  • Product is active for the sales channel and USA market
  • USA market is enabled in settings
  • Stock is available
  • Product/variant queries execute successfully

Root cause identified:
USA customers accessing via the Mobile Buy SDK are incorrectly categorized as being from other countries. When testing with VPN, the default shipping country showed as Austria/Spain despite connecting through USA servers, suggesting Shopify’s market detection mechanism is failing for USA users specifically.

Attempted solutions:

  • Updating API version (changed error message but didn’t resolve issue)
  • Querying variant availability by market (API doesn’t support this field)
  • Confirmed with real USA-based user (issue persisted)

Current workaround:
Make the product available to ALL markets (including “International”), then use a third-party app like LockSmith to hide it from non-USA customers on the web storefront based on IP address detection.

Status: Appears to be a Shopify platform bug affecting USA customers on iOS Mobile Buy SDK. Shopify support showed limited interest in investigating.

Summarized with AI on November 20. AI used: claude-sonnet-4-5-20250929.

I’ve seen this issue/error posted about multiple times, but can’t find any on this forum that actually have a solution. I’m using the Mobile Buy SDK for iOS. I’ve successfully implemented “Buy with Apple Pay” and web checkout buttons in my app for a particular product. The product has only 1 variant.

If I make a purchase in Australia, UK, or the EU, it works fine. If I try to create the checkout using a VPN connected in the USA (i.e. pretending i’m a USA customer), I get the following error from the API: User error: Variant is invalid.

Because all other regions work fine, and even the USA is able to query the product and variant, I’m certain my process for querying products and variants is not the issue. Things I have checked:

  • The product is active for the sales channel
  • The product is active for the USA market
  • The USA market is active in settings > markets
  • The product has stock available on hand
  • Tried creating a new product, issue persists with new product also

This is how I query for product and variant (and it works successfully)

let productId = "123456"
let query = Storefront.buildQuery { $0
                .node(id: productId!) { $0
                    .onProduct() { $0
                        .id()
                        .title()
                        .tags()
                        .description()
                        .availableForSale()
                        .totalInventory()
                        .images(first: 1) { $0
                            .edges { $0
                                .node { $0
                                    .url()
                                }
                            }
                        }
                        .priceRange() { $0
                            .maxVariantPrice() { $0
                                .amount()
                                .currencyCode()
                            }
                        }
                        .variants(first: 1) { $0
                            .edges { $0
                                .node { $0
                                    .id()
                                    .title()
                                    .availableForSale()
                                    .currentlyNotInStock()
                                    .unitPrice() { $0
                                        .currencyCode()
                                        .amount()
                                    }
                                }
                            }
                        }
                    }
                }
            }
let task = client!.queryGraphWith(query) { [self] response, error in
    if let response = response {
        product = response.node as? Storefront.Product;
        let variants = product!.variants.edges.map({$0.node});
        variant = variants.first;
    }
};
task.resume();

This is my checkout create input (fails for USA only, works for all other regions)

let input = Storefront.CheckoutCreateInput.create(
            lineItems: .value([
                Storefront.CheckoutLineItemInput.create(quantity: 1, variantId: variant!.id),
            ]),
            customAttributes: .value([
                Storefront.AttributeInput(key: "user_id", value: currentUserId() ?? ""),
            ]),
            allowPartialAddresses: .value(true),
            presentmentCurrencyCode: .value(currencyCode)
        )
let mutation = Storefront.buildMutation { $0
            .checkoutCreate(input: input) { $0
               ...
                .checkoutUserErrors { $0
                    .field()
                    .message()
                }
            }
        }
        
        let task = client!.mutateGraphWith(mutation) { result, error in
            guard error == nil else {
                callback(nil);
                return
            }
            guard let userErrors = result?.checkoutCreate?.checkoutUserErrors else {
                callback(nil);
                return
            }

            if (userErrors.count > 0) {
                //THIS IS WHERE "invalid variation id" error is captured
                callback(nil);
                return;
            }
            self.checkout = result?.checkoutCreate?.checkout
            callback(self.checkout);
        }
        task.resume()
    }

Hello @jarrad777

I think I know what the problem is. The variantId you are passing to the CheckoutCreateInput is not the ID of the variant that is available in the USA market. To find the ID of the variant that is available in the USA market, you can use the following query:

query {
  node(id: "123456") {
    onProduct {
      variants(first: 1) {
        edges {
          node {
            id
            availableInMarkets {
              code
            }
          }
        }
      }
    }
  }
}

This query will return the ID of the variant that is available in the USA market. You can then pass this ID to the variantId property of the CheckoutCreateInput.

Here is an updated version of your code that should fix the problem:

let productId = "123456"
let query = Storefront.buildQuery { $0
                .node(id: productId!) { $0
                    .onProduct() { $0
                        .id()
                        .title()
                        .tags()
                        .description()
                        .availableForSale()
                        .totalInventory()
                        .images(first: 1) { $0
                            .edges { $0
                                .node { $0
                                    .url()
                                }
                            }
                        }
                        .priceRange() { $0
                            .maxVariantPrice() { $0
                                .amount()
                                .currencyCode()
                            }
                        }
                        .variants(first: 1) { $0
                            .edges { $0
                                .node { $0
                                    .id()
                                    .title()
                                    .availableForSale()
                                    .currentlyNotInStock()
                                    .unitPrice() { $0
                                        .currencyCode()
                                        .amount()
                                    }
                                    .availableInMarkets {
                                      code
                                    }
                                }
                            }
                        }
                    }
                }
            }
1 Like

I have a separate product for each market. This is the only product of this type available in the USA market, and it only has the 1 variant which therefore should also be available in the USA market. This product is not shared by other markets - each market has its own seperate, though identical, product.

Also, this error is returned if I try querying in this manner:

Value of type ‘Storefront.ProductVariantQuery’ has no member ‘availableInMarkets’

1 Like

Note, after updating the API version, the error now reads "Variant product is not published for this customer".

I’ve noticed that if I enable the product for the “International” market which includes all other countries I don’t ship to in addition to “usa”, it now works. So it seems customers coming from the USA aren’t being recognised as USA customers but perhaps some other country?

As a workaround, I can make this product available to all markets, and use a third party app like LockSmith to hide the product from other regions on the shopfront, but it seems very clunky.

Does anyone know how Shopify determines what market a user belongs? Is it possible my VPN is being categorized as a non-USA country, even though its connecting to a server in the USA? I did notice when I proceed with web checkout using VPN, the default shipping country is Austria, but 10 mins ago was Spain. So perhaps its an issue detecting the market of the user based on ip address?

Potential bug with Shopify. Users coming from the USA via the Mobile Buy SDK for iOS are being treated as customers in other countries (hence, if your product is only available for the USA market, it will get the invalid error). Haven’t tested for the Android SDK, and only seems to impact USA customers. I do not think this issue was related to the VPN service we used because we also had a real person based in the USA try to make a purchase and they also had this issue.

Shopify support seem uninterested in the issue. The workaround for me was to make the product available for ALL markets (seeing as I didn’t have the patience to test all 200+ countries to see which one they were being grouped to). To avoid the product being shown on the web storefront for non-USA customers, I used a third party app like LockSmith to hide it. It’s not a great solution and comes at a small cost, but the only one I could think of to get around the apparent bug.