Every row must carry a Handle
Reported as Handle missing
Fix. Fill in the handle of the product the row belongs to. A blank handle does not inherit from the row above.
Stated by Shopify’s documentation
Specification
It looks like a spreadsheet and it is not one. Meaning is carried between rows, and most of the ways it destroys a catalogue produce no error message at all. This is the whole rule set, with the evidence behind each one.
A Shopify product CSV is not rows of products. It is a flat serialisation of a three-level tree — the file holds handle groups, each handle group is one product, and each product has both variants and images.
Because variants and images are lists of different lengths, the serialisation pads. A product with 2 variants and 9 images occupies 9 rows. Rows three to nine are neither products nor variants. They are image rows, and almost every rule that applies to a variant row is wrong about them.
So the single most important thing in this document is that a row has a role, and which rules apply depends on the role. This is not a nicety. Applying one option rule to every row of a real 590-row export fires 523 times, of which 519 are image-only rows and therefore false alarms — a report that is 99.2% noise. A merchant who scrolls past five hundred wrong answers will not read the right one.
One more distinction that the first draft of this document got backwards, and that cost us a wrong rule: option names are product-scoped and option values are variant-scoped. Option1 Name is declared once, on the product row; each variant row carries its own Option1 Value. Requiring the name on every variant row reported 200 blockers on a 204-row file that Shopify imports without complaint.
And what groups a product is the handle, and only the handle. Not the title, not the SKU, not position in the file. Two rows sharing a handle are the same product even if their titles differ and even if they are a hundred rows apart — which is the mechanism behind the most destructive rule below.
Image Src, everything else blank. One additional image, and nothing else.Classify the row before applying a rule to it.
Every rule carries a severity and the evidence it rests on, and the second one governs the first.
A rule may only be a blocker on documentary or observed evidence. Anything resting on a third-party assertion, or on an Admin API probe, is capped at warning no matter what it declares.
A measurement may lower a severity and may never raise it. If the API rejects something, the importer might still accept it — the file never reaches that layer — so an observed rejection is not grounds for telling you your import will fail. If the API accepts something, we have caught ourselves crying wolf, and one false alarm costs the whole report its credibility.
The severities printed below are the effective ones, after that cap.
These are the rules a per-row tool cannot see, because the thing they are about happens between rows. Every one of them is invisible to a spreadsheet and to a language model reading your file a chunk at a time.
Reported as Handle missing
Fix. Fill in the handle of the product the row belongs to. A blank handle does not inherit from the row above.
Stated by Shopify’s documentation
Reported as Handle will be rewritten by Shopify
Fix. Rename the handle to the lowercase, hyphenated form you actually want the URL to use.
Measured against a real store
What this looks like when it happens to you: Shopify imported fewer products than my CSV had, and reported success · Shopify changed my product handles, so my URLs are not what I uploaded
Reported as Two different products share one handle
Fix. There is no automatic answer to this one — which of the two things you meant is a decision only you can make. We name it and stop.
Stated by Shopify’s documentation
What this looks like when it happens to you: Shopify imported fewer products than my CSV had, and reported success · How many products are actually in my Shopify CSV?
Reported as Title repeated on a continuation row
Fix. Clear Title, Body (HTML), Vendor and Tags on every row after the first row of a handle.
Stated by Shopify’s documentation
Reported as Variants with no option to tell them apart
Fix. Put Option1 Name on the product row, and an Option1 Value on every variant row. A single-variant product may leave both blank; Shopify assigns "Default Title".
Stated by Shopify’s documentation
What this looks like when it happens to you: Re-importing a product deleted its variants, and nothing warned me
Reported as Variant row missing inventory policy or fulfillment service
Fix. Put a Variant Inventory Policy ('deny' or 'continue') and a Variant Fulfillment Service ('manual', or your app's handle) on every variant row, including continuation rows.
Stated by Shopify’s documentation
What this looks like when it happens to you: Shopify: "Fulfillment service can't be blank" · Shopify: "Inventory policy is not included in the list" · Shopify said "Products Imported" and a product never arrived
Reported as Product has no name
Fix. Put a Title on the first row of every handle group. Continuation rows must still leave it blank (SH-004).
Stated by Shopify’s documentation
Reported as Duplicate variant option combination
Fix. Give each variant a distinct combination of option values, or remove the duplicate row.
Stated by Shopify’s documentation
Reported as Variant gives the wrong number of option values
Fix. Give every variant a value for each option the product declares.
Stated by Shopify’s documentation
Reported as Empty continuation row
Fix. Delete the row. It carries a handle and no data.
Stated by Shopify’s documentation
Reported as More than 250 images on one product
Fix. Reduce the product to 250 images or fewer.
Stated by Shopify’s documentation
Reported as More than 250 tags on one product
Fix. Reduce the product to 250 tags or fewer.
Stated by Shopify’s documentation
Properties of the file rather than of any row in it. A file that fails one of these has no failing rows to point at, which is why a report that only counts bad rows will call it clean.
Reported as File is not UTF-8
Fix. Re-save the file as UTF-8. This is safe and loses nothing.
Stated by Shopify’s documentation
What this looks like when it happens to you: A tool says my CSV is not UTF-8, but it looks fine
Reported as Windows (CRLF) line endings
Fix. There is no automatic answer to this one — which of the two things you meant is a decision only you can make. We name it and stop.
Stated by Shopify’s documentation
What this looks like when it happens to you: Are Windows line endings (CRLF) breaking my Shopify import?
Reported as Header spelling does not match Shopify
Fix. Correct the header spelling, including capitalisation.
Stated by Shopify’s documentation
Reported as File is too large for the importer
Fix. Split the file, keeping every handle group whole.
Stated by Shopify’s documentation
What this looks like when it happens to you: Shopify: "The supplied file is too large to be imported." · My Shopify CSV import has been running for hours — is it stuck?
Character budgets. Nothing else in the ecosystem counts these for you, and the family splits in a way worth knowing: one of them is a hard rejection and the others are display advice that no platform enforces. We measured which is which rather than assuming.
Reported as SEO Title over 70 characters
Fix. Rewrite the title to fit. Do not simply cut it — a title truncated mid-word reads as broken.
Measured against a real store
Reported as SEO Description over 320 characters
Fix. Rewrite the description to fit rather than truncating it mid-sentence.
Measured against a real store
Reported as Image Alt Text over 512 characters
Fix. Shorten the alt text. It describes the image for screen readers; it is not a keyword field.
Measured against a real store
Reported as Alt text longer than our 125-character recommendation
Fix. Aim for around 125 characters. Alt text is read aloud in one breath by a screen reader.
Third-party assertion only — not in Shopify’s docs
It is not Shopify’s documentation and it does not speak for them. It is our working model of a format, assembled from their documentation where that exists, from real supplier files where it does not, and from breaking a real store on purpose where neither answered.
It is not complete. Some rules we would like to have are not here because we cannot establish them honestly, and the methodology page names them. A specification that claims to be finished is making the same promise this whole product refuses to make.
And a platform can change its behaviour at any time. This is a maintenance commitment, not a finished artifact — which is one of the reasons we never claim your import is guaranteed to succeed.
All 20 of them, free, with the rows named. No account, and the file is deleted in the same request that reads it.
Check a file free