Has anyone run into something like this?
I add a product to the cart from the product page.
In the current Market (Poland [PLN]) my cart validation function doesn’t populate errors for ValidationAddOperation, so the product is added successfully.
However, if I switch to another Market on the product page (for example, selecting United States [USD] in the store switcher), and in that Market the validation function does return errors, a redirect loop occurs (ERR_TOO_MANY_REDIRECTS) on the product page:
POST: ``https://test.myshopify.com/en-poland/localization
request params:
country_code: US
return_to: /en-poland/products/tttt
302
response location:https://test.myshopify.com/services/currency/update?currency=USD&country=US&return_to=%2Fproducts%2Ftttt
Then it loops:
== 302 ==>
GET ``https://test.myshopify.com/products/tttt
== 302 ==>
GET ``https://test.myshopify.com/services/currency/update?currency=USD&country=US&return_to=%2Fproducts%2Ftttt
== 302 ==>
GET ``https://test.myshopify.com/products/tttt
== 302 ==>
…
If I switch delivery countries on the checkout page using the same steps, everything works correctly - the validation message appears as expected.
I’m intentionally not posting my specific validation function code here because it’s not related to the issue.
This can be reproduced very easily in a dev environment:
create an empty validation function → add a product to the cart → then hardcode any errors in the function - try switching Markets from the product page.
use crate::schema;
use shopify_function::prelude::*;
use shopify_function::Result;
#[shopify_function]
fn cart_validations_generate_run(input: schema::cart_validations_generate_run::Input) -> Result<schema::CartValidationsGenerateRunResult> {
let mut operations = Vec::new();
let mut errors = Vec::new();
// errors.push(schema::ValidationError {
// message: format!(
// "Invalid!"
// ),
// target: "$.cart".to_string(),
// });
let operation = schema::ValidationAddOperation { errors };
operations.push(schema::Operation::ValidationAdd(operation));
Ok(schema::CartValidationsGenerateRunResult { operations })
}
Hi @Siarhei_Shyshko
I believe this redirect loop isn’t caused by a bug in your Cart & Checkout Validation function implementation, but by how it interacts with Shopify’s localization/Markets flows on Online Store - so this appears to be a platform limitation rather than an implementation issue on your side.
When you switch Markets on the product page, Shopify posts to /localization, which redirects to /services/currency/update to update the cart’s currency/market, and then redirects back to the product URL. If your validation function returns a blocking ValidationAddOperation error at $.cart during that cart update, the update is treated as invalid, and the platform keeps bouncing between /products/... and /services/currency/update without ever resolving, causing ERR_TOO_MANY_REDIRECTS.
On checkout, the same validation errors behave as expected because the checkout UI is designed to show those errors inline and keep the user on the same page. The problem specifically appears when a cart-affecting operation (like updating market/currency via /services/currency/update) happens behind a redirect-based flow on the storefront. Your “minimal repro” (empty function → add product → hardcode a cart-level error → switch Market on the product page) is therefore enough to trigger this behavior, even though the function itself is valid and doing what the API allows.
Since you can’t change how /localization and /services/currency/update work, the practical workaround is to only emit blocking validation errors when the context indicates the buyer is actually in checkout, and not for generic cart or localization recalculation events. That means using fields from the Cart & Checkout Validation input to distinguish “checkout in progress” from “simple cart update / internal recalculation” and skipping ValidationAddOperation errors outside checkout. This avoids triggering the redirect loop, while still enforcing your rules at checkout.
1 Like
Thank you very much for the clarification @Liam-Shopify
I’ll limit the validation logic to run only during checkout.
There is just one point I’d like to better understand. Since validation functions cannot reliably affect behavior on pages other than checkout (and may even lead to redirect loops), and there is no UI on those pages to display validation messages, I’m curious about the intended purpose of running the validation function on those pages by default from Shopify’s side.