Shopify CSV import error messages 2026: “Invalid CSV header: missing headers” and 19 more

You upload a product CSV and Shopify hands it straight back: “Invalid CSV header: missing headers”. No line number. No hint about which header is missing. Just a refusal, and a spreadsheet you now have to inspect column by column, hoping the answer is obvious.
We build Rubik Variant Images and Swatch, which assigns a separate image set to every variant of a product, so a good share of the questions we get come from merchants in the middle of a bulk import. Which means we have read a lot of these strings. Some of them are clear. Some name the wrong thing entirely.
So this post is the dictionary. Twenty error strings the product CSV importer actually emits, copied character for character, each with the cell you have to change. Copy your error out of the admin, hit your browser’s find, jump to it.
One thing before the list. Almost every one of these is a formatting problem in a single cell, not a broken file. The importer is much stricter than it’s talkative, and it validates row by row, which is how one bad column produces forty errors.
In this post
- What Shopify CSV import error messages actually mean
- Every error string, verbatim
- Why the Handle column decides everything
- Why does Shopify say “Invalid CSV header: missing headers”?
- Inventory, price, category and fulfillment errors
- Image errors, and the 20 versus 25 megapixel mess
- The import worked and the gallery is still wrong
- Variant errors: Default Title, duplicate options, daily limits
- How to stop the whole family of errors happening again
- Frequently asked questions
What Shopify CSV import error messages actually mean
Short answer: Shopify CSV import errors are validation failures in single cells, not a broken file. Some name the offending line, some refuse the file before reading a row. A clean import still leaves image mapping open, which is why Rubik Variant Images assigns a separate image set to every variant afterwards.
The messages come in three families, and telling them apart saves you an hour of guessing.
- File level refusals. Nothing imported, and no line number either, because the parser never got as far as your data.
Invalid CSV header: missing headers,Illegal quoting on lineandMissing or stray quote on lineall live here. - Skipped rows. The import finished, and quietly left some rows out. Anything starting with
Ignored lineis this. Your product count will be lower than your row count and nobody will stop you. - Model rejections. The row reached Shopify’s product model and failed a rule there. These all start with
Validation failed:and they are the most specific, because they name the attribute.
The line numbers look like #-## in the documentation because the importer reports a range: a parent row plus its variant rows count as one product, so the range covers the whole block. Row 2 through row 5 is one product with four variants, not four products.
Every error string, verbatim
Here is the full set, character for character, so you can match yours without squinting. Eighteen of them are on Shopify’s own common product CSV import problems page; the last two only turn up in the admin. Detail on each one follows below.
Invalid CSV header: missing headersIgnored line #-## because handle example already existsIgnored line #-## because it did not contain product dataIllegal quoting on lineMissing or stray quote on lineIncorrect header checkInventory policy is not included in the listInventory quantity can't be blankInventory quantity is not a numberNot a valid product categoryValidation failed: An error occurred while trying to download the imageValidation failed: getaddrinfo: nodename nor servname provided, or not knownValidation failed: options are not uniqueValidation failed: price can't be blankValidation failed: The uploaded image exceeds the 20 megapixel limitValidation failed: Value must be a valid product referenceFulfillment service can't be blankDaily variant creation limit reached, try againImport job already in progress. Please wait until current one is finishedLine 2-5: Validation failed: The variant Default Title already exists
Why the Handle column decides everything
Before the individual errors, the model. Get this wrong and half the list above fires at once.
Handle is the spine of a Shopify product CSV. It is the URL slug, and the importer uses it as the identity of the product. Rows that share a Handle are one product. That’s the entire grouping mechanism. There’s no product ID column doing the work behind the scenes.
So a product with four colours is four rows, not one. The first row is the parent: it carries Handle, Title, Body HTML, Vendor, Type, Tags, Published, the option names, and the first variant. Rows two through four repeat the Handle, leave Title and every other product level column empty, and fill only the variant columns. Option1 Value, price, SKU, barcode, weight, inventory.
Why leave Title blank on the variant rows? Because a filled Title on row three tells the importer “this is another product”, and a Title on a row whose Handle already belongs to a product is where Ignored line #-## because handle example already exists comes from. The word example is literal in the docs; in your email it will be your actual handle.
And here is the part that deserves more anger than it gets: a duplicate Handle doesn’t error out by default. It overwrites. Import a file where two unrelated products both ended up as blue-shirt and the second one silently replaces the first, in a system with no undo. The importer will warn you about a blank inventory cell but not about destroying a product record. That ordering of priorities is indefensible.
Same trap with the other direction: a row that has a Handle but no Title and nothing usable in the variant columns gets Ignored line #-## because it did not contain product data. Usually a stray blank row your spreadsheet added at the bottom, or a separator row you left in for readability. Delete it and re-upload. If you are building the file from scratch rather than editing an export, our free Product CSV Generator lays the parent and variant rows out correctly so you never hit either message.
For the full column by column walkthrough, the Shopify product CSV import guide covers the procedure. This post covers what happens when the procedure bites back.
Why does Shopify say “Invalid CSV header: missing headers”?
Because a required column is not there, or the importer cannot see it. Shopify’s own wording is blunt about the requirement: the first line has to carry the complete header row, every column from Handle through Image Alt Text. Three causes, in order of how often they turn out to be the real one: the Handle column is missing or renamed, the header row is not the first row of the file, or the file is not actually comma separated (a semicolon delimited export from a European locale looks like one giant column to the parser).
| Error string | What actually happened | What to change |
|---|---|---|
Invalid CSV header: missing headers | A required column is absent, renamed, or the header row is not row 1. Semicolon delimiters cause it too. | Put Handle in the first column, keep the header row as row 1, delete any title or notes rows above it, re-export with commas. |
Ignored line #-## because handle example already exists | Two rows in the file claim the same Handle as separate products, or the handle exists in the store. | Rename one handle, or blank the Title on the follow up rows so they read as variants. |
Ignored line #-## because it did not contain product data | The row has a Handle and nothing else the importer can use. | Fill Title on the parent row, or delete the empty row. |
Illegal quoting on line | A double quote inside a cell is not escaped, so the parser loses track of where the field ends. | Double any literal quote inside a quoted cell, replace curly quotes with straight ones, re-save as CSV UTF-8. |
Missing or stray quote on line | Same cause, different symptom: an opening quote with no closing partner. | Open the file in a plain text editor, go to that line, close the quote. |
Incorrect header check | Nothing to do with your header row. It comes from the image decoder on that line failing to decompress the file. | Shopify’s own first step: clear Image Src on that line and re-import to confirm it is the image. Then re-save the file as a real JPEG or PNG and host it somewhere else. |
Incorrect header check is the worst named error in the set. It sounds like the CSV header row. It is a decompression failure on the image referenced by that line, which is a completely different part of the pipeline. Merchants spend twenty minutes rewriting a header row that was fine.
Excel is the enemy here, and Google Sheets mostly is not
Both quoting errors and a good share of the field errors trace back to how the file was saved.
Excel’s plain “CSV (Comma delimited)” option writes the file in the system code page rather than UTF-8 (which is why the same file opens fine on your own machine and arrives mangled at Shopify), so every accented character in a French or Turkish product title turns into a mojibake. It strips leading zeros from SKUs and barcodes. It converts a 13 digit barcode to scientific notation. It reads 1/2 in a size column as a date. And any text you pasted in from a word processor or a web page arrives carrying curly quotes and non-breaking spaces, which is exactly what produces Illegal quoting on line.
Why does Excel still default to the system code page in 2026? No idea. The UTF-8 option has been sitting in that same dropdown for years and it still is not the one you get by pressing Save.
If you must use Excel, pick “CSV UTF-8 (Comma delimited)” and format the SKU and barcode columns as Text before you touch them. Google Sheets exports UTF-8 by default and leaves numeric strings alone, which is why we suggest it for anyone editing a Shopify export. Not because it’s a better spreadsheet. Because it does less.
Before you upload, run the file through our free Product CSV Validator. It checks the header row, the Handle grouping and the quoting, which is the three most common failures in one pass.
Inventory, price, category and fulfillment errors
These are the friendly ones. Each names its attribute, each is one cell.
| Error string | What actually happened | What to change |
|---|---|---|
Inventory policy is not included in the list | Variant Inventory Policy accepts two values only. Anything else, including a blank in a file that has the column, is rejected. | Write deny or continue, lowercase, in every variant row. |
Inventory quantity can't be blank | The file has an inventory quantity column for a location but left the cell empty. | Write 0 rather than leaving it blank. Blank is not zero to the importer. |
Inventory quantity is not a number | The cell holds text, a thousands separator, a currency symbol, or a formula that resolved to an error. | Strip commas and symbols, paste as values, keep it a bare integer. |
Validation failed: price can't be blank | Variant Price is empty on a variant row. Every variant needs its own price, including row three of a four colour product. | Fill Variant Price on every row that has an Option1 Value. |
Fulfillment service can't be blank | Variant Fulfillment Service is empty, or names a service that is not installed on the store. | Write manual unless you genuinely use a third party service, in which case use its exact handle. |
Not a valid product category | Product Category must match a node in Shopify’s Standard Product Taxonomy exactly, either the full breadcrumb or the category ID. Close isn’t enough, and the breadcrumb is long. | Set the category on one product in the admin, export that product, copy the string it produced. Or paste the taxonomy category ID instead, which the importer accepts in the same column. |
Validation failed: Value must be a valid product reference | A metafield column points at a product that does not exist in the store yet, so the reference cannot resolve. | Import in two passes: products first, then a second file that only sets the reference metafields. |
That last one catches people migrating a catalogue with related product metafields. There is no ordering trick that saves you, because the reference has to resolve at write time and the target row may not have been processed yet. Two passes. Always.
The category one is worth a specific warning. Shopify’s taxonomy strings look like Apparel & Accessories > Clothing > Shirts & Tops, with a specific ampersand and specific spacing, and typing it from memory fails. Two ways out, both faster than reading the taxonomy: export a product that already has the right category and copy the value, or drop the breadcrumb entirely and put the taxonomy category ID in the cell, since the importer will match on either.
Image errors, and the 20 versus 25 megapixel mess
Three image errors, and one genuine contradiction inside Shopify itself.
| Error string | What actually happened | What to change |
|---|---|---|
Validation failed: An error occurred while trying to download the image | Shopify fetched the Image Src URL and did not get an image back. Google Drive and Dropbox share links return an HTML page, not a file. Signed URLs expire. Hotlink protection blocks the fetch. | Use a direct file URL that returns the image bytes to an anonymous request. Test it in a private browser window first. The Admin API counterpart of this is IMAGE_DOWNLOAD_FAILURE. |
Validation failed: getaddrinfo: nodename nor servname provided, or not known | A DNS lookup failed, almost always because the URL has no scheme. cdn.example.com/red.jpg is not a URL Shopify can resolve. | Prefix every Image Src with https://. Check for a leading space in the cell too, spreadsheets love adding one. |
Validation failed: The uploaded image exceeds the 20 megapixel limit | Width times height is over the ceiling the importer enforces. A 6000 x 4000 camera file is 24 megapixels. | Resize before hosting. Also check the aspect ratio: Shopify requires something between 100:1 and 1:100, so a stitched panorama gets refused on shape alone. |
Now the contradiction. That error says 20 megapixels. Product and collection images in the admin accept up to 25 megapixels, which is 5000 x 5000. The Files area and theme assets stop at 20. So the same photo can be accepted through one door and refused through another, and the number you get told depends entirely on which door you used.
| Where you upload | Real resolution ceiling | File size ceiling | What the error text says |
|---|---|---|---|
| Product and collection images, admin | 25 MP, up to 5000 x 5000 | 20 MB | 25 megapixels |
| Content, then Files | 20 MP | 20 MB | 20 megapixels |
| Theme assets and blog images | 20 MP | 20 MB | 20 megapixels |
| Product CSV import, Image Src | enforced at 20 MP | 20 MB | exceeds the 20 megapixel limit |
Is a 22 megapixel photo too big for Shopify? Depends which Shopify you ask. Drag it onto a product and it goes in. Reference the same file from a CSV and it comes back rejected with a number that does not match the product image ceiling. We have read a lot of Shopify error strings and this one still irritates us, because it isn’t a hard problem to fix: print the limit for the surface the merchant is actually using. The 25 MP image limit error fix post goes through the resize paths in detail, including the batch ones.
A few more filename traps that produce no error at all, which makes them worse. Shopify image filenames cannot end with pico, icon, thumb, testing, small, compact, medium, large or grande, because the CDN reads those as resize instructions. So hoodie_blue_large.jpg is a trap, and agencies export files named exactly like that all day. Filenames also cannot begin with a period, and putting _ immediately before the dimensions in a name confuses the CDN too. Our free Bulk Image Renamer strips those suffixes across a whole folder, and the image filename guide explains the naming pattern we recommend instead.
The import worked and the gallery is still wrong
You cleared every error. Zero skipped lines. The email says success. Open a product and the gallery is a pile of sixty photos in import order, and a shopper who clicks Blue sees one blue thumbnail followed by every red and green shot you own.
That isn’t a bug in your file. It’s a gap in the format. A Shopify product CSV has Image Src, Image Position, Image Alt Text and Variant Image. Variant Image takes exactly one URL per variant row. There is no column anywhere in the specification that says “these four photos belong to Blue”. The importer can attach a whole gallery to the product, and it can point each variant at a single featured image, and that’s the entire vocabulary it has.
Which means a technically perfect import still ships a product page that shows the wrong photos. This is one of the most common things we get asked about, and it never arrives labelled as an import problem, because the import said success.
The free fix, and it does work: open each product in the admin and assign one image per variant by hand, then delete or reorder the leftovers so the gallery reads sensibly. Small catalogues, do that. The variant images without an app post covers the manual methods and the theme level workarounds honestly, including where they break.
Doing it by hand is realistic at twenty products. On a catalogue you just imported from a supplier feed, with 300 products and 8 colours each, that is 2,400 assignments and you redo them the next time the feed changes, which is why Rubik Variant Images assigns a full image set to every variant instead of one featured photo, stored in product metafields so the storefront filters the gallery with no external API calls. Free plan to start, 5.0 stars across 420 reviews.

Two assignment paths matter after an import, and they work differently, so pick deliberately.
- Bulk assign reads gallery order. Each variant’s first media acts as a boundary, and the images that follow it in gallery order inherit that variant until the next boundary. It processes hundreds of products in the background and makes no AI calls. That’s the one to use after a CSV import, because Image Position gave you a deliberate order and that order is real information.
- AI auto-assign works one product at a time. It reads the product title, the variant name, the image filename and the image alt text, and matches images to variants from those. Use it on the products the order based pass got wrong, not as your first pass on 300 products.
We chose gallery order rather than filename matching for the bulk path on purpose. Filenames coming out of a supplier feed are chaos, and a matcher that trusts them fails quietly, which is the worst way to fail. Order, at least, is something you controlled when you built the file.
If you want the CSV side of this specifically, we wrote up what to do about variant images after a CSV import on the Rubik Variant Images site, and bulk assigning multiple variant images from a CSV covers the spreadsheet side here.
Variant errors: Default Title, duplicate options, daily limits
Four errors that all show up during migrations, and one of them has a hard number attached that almost nobody quotes.
| Error string | What actually happened | What to change |
|---|---|---|
Validation failed: options are not unique | Two variant rows on the same Handle carry the identical combination of Option1, Option2 and Option3 values. Trailing spaces count, so Blue and Blue collide too. | Sort by Handle then by option values, find the duplicate pair, trim whitespace across the option columns. |
Line 2-5: Validation failed: The variant Default Title already exists | The product already has the single unnamed variant Shopify creates for option free products, and your file is trying to add another variant without giving it an option value. | Give every variant row a real Option1 Name and Option1 Value, or import the product fresh rather than updating over the Default Title variant. |
Daily variant creation limit reached, try again | Stores holding 500,000 or more variants cannot create more than 10,000 new variants through CSV upload inside any 24 hour window. Shopify Plus stores are exempt. | Count the new variants in the file, split it into chunks under ten thousand, and run the rest once the window rolls over. |
Import job already in progress. Please wait until current one is finished | One import runs at a time per store, and a large file can still be working long after the browser tab moved on. | Wait for the completion email, and do not re-upload, because that is how duplicate handles overwrite each other. A job still stuck hours later has to be cleared by Shopify support; there is no cancel button for it in the admin. |
The Default Title one is a migration classic. Anything imported without options gets a variant literally named Default Title, and later files that try to add real variants on top of it collide. The Admin API equivalent you will see if you are working through GraphQL instead is VARIANT_ALREADY_EXISTS.
The daily variant creation limit is 10,000 new variants per 24 hours
Shopify is specific about this one, which makes it odd how much hand waving you find about it elsewhere. A store holding 500,000 or more variants cannot create more than 10,000 new variants through CSV upload inside any 24 hour window. Below that variant count the ceiling does not apply, and Shopify Plus stores are not subject to it at all. Both numbers come from Shopify’s common import problems page, not from us.
So the fix is arithmetic rather than luck: count the new variants in the file, cut it into chunks under ten thousand, run the rest after the window rolls over. Worth sitting with that threshold for a second, though. Half a million variants in one store. If you are anywhere near it, the real question is whether your catalogue should be carrying that many variants per product at all. Our free variant combination calculator shows how fast option counts multiply, and the Shopify variant limit post covers the per product ceilings.
There is a structural answer too. Splitting one 400 variant monster into separate colour products and then grouping those products back together with combined listings keeps variant counts per product low while the storefront still shows one listing with swatches. Read the limits carefully here, though, because two different things get called combined listings. Shopify’s own Combined Listings app caps a listing at 60 products, 3 extra options and 2,000 variant option values across all children, and it is gated to Plus and Enterprise, which makes it academic on Basic or Grow. Rubik Combined Listings runs on every plan and is free for 5 groups. That plan gate is more or less the whole reason it exists.
How to stop the whole family of errors happening again
Six habits, in the order they pay off.
- Export before you import. Take a Shopify export of the products you are about to touch, edit that file, and upload it back. The columns are then guaranteed correct and you have a rollback copy.
- Test on five rows. Import a five row slice first. Every error in this post shows up on five rows just as reliably as on five thousand (the validator does not care how big your ambitions are), and it takes seconds rather than an hour.
- Save as CSV UTF-8, always. Or edit in Google Sheets and skip the question.
- Format SKU, barcode and any size column as Text before editing, so nothing gets helpfully converted.
- Sort by Handle and scan for accidental duplicates. This is the only one of these habits that protects you from data loss rather than from an error message.
- Host images somewhere that returns bytes to anonymous requests, with names that do not end in the CDN’s reserved suffixes.
Worth knowing about storage while you are bulk importing images: file allowances run 100 GB on Starter, Pause and Build, Retail and Basic, 300 GB on Grow, 500 GB on Advanced, 1 TB on Plus and 10 TB on Enterprise, with video and 3D counted separately on top. Deleting files doesn’t free the quota immediately either, which is why merchants keep reporting that they cleared a pile of files and are still blocked; the recalculation can take up to 24 hours to catch up. The file storage limits post has the detail.
“I ran into an issue where some variant images weren’t showing up correctly, and it turned out to be a filename problem. Farid was amazing, super responsive and went above and beyond to help me troubleshoot and even updated the theme code so I won’t run into this issue again. The app works perfectly now and has made managing variant images so much easier. Highly recommend, and big thanks to Farid!”
Anonymous merchant, 2025-07-24, Rubik Variant Images and Swatch on the Shopify App Store
One last habit, for anyone importing with variants regularly: read how to import products with variants once properly, and how to match product images to variants automatically afterwards. Between those two the import stops being an event.
See it running on the live demo store, watch the bulk assign walkthrough, or read the getting started guide. Rubik supports 350+ themes natively, so there are no theme file edits in any of it.
Frequently asked questions
What does “Invalid CSV header: missing headers” mean in Shopify?
It means the importer could not find a column it needs, so nothing was imported. Usually the Handle column is missing or has been renamed, the header row is not the first row of the file, or the file is semicolon delimited rather than comma delimited. Put Handle in the first column, delete any rows above the header row, and re-export as CSV UTF-8 with commas.
Why does Shopify say a line was ignored because the handle already exists?
Because two rows in your file both look like parent products with the same Handle. Rows sharing a Handle are meant to be one product with several variants, and only the first row carries the Title. If row three has both the same Handle and a Title, the importer treats it as a second product claiming an existing handle and skips it. Blank the Title on the variant rows.
Does the Shopify CSV importer overwrite existing products?
Yes, and quietly. Handle is the identity key, so a row whose Handle matches an existing product updates that product rather than creating a new one. There is no confirmation step and no undo. Sort your file by Handle and check for accidental duplicates before every import, and keep the export you started from as your rollback copy.
How do I stop Excel from breaking a Shopify CSV?
Save as “CSV UTF-8 (Comma delimited)” rather than plain CSV, and format SKU, barcode and size columns as Text before you edit them. That prevents the encoding damage behind quoting errors, the stripped leading zeros, and the barcode turned into scientific notation. Editing the file in Google Sheets avoids all three, because it exports UTF-8 and leaves numeric strings as strings.
What is the daily variant creation limit in Shopify?
A store holding 500,000 or more variants can create no more than 10,000 new variants through CSV uploads in any 24 hour window, per Shopify’s Help Center. Stores under that variant count are not affected, and Shopify Plus stores are exempt. If you see Daily variant creation limit reached, try again, count the new variants in your file, split it into chunks under ten thousand, and import the rest after the window rolls over.
Which app should I use to fix variant images after a CSV import?
Rubik Variant Images and Swatch, built by Craftshift. A Shopify CSV can only give each variant one featured image, so the gallery stays wrong even after a clean import. Rubik assigns a full image set per variant through product metafields and filters the gallery on selection, with a background bulk pass that reads gallery order. Free plan, 5.0 stars across 420 reviews.
Can I assign multiple images per variant with a CSV?
No. The Variant Image column accepts one URL per variant row, and no column in the format expresses “these four photos belong to Blue”. You can import the whole gallery and one featured image per variant, then map the rest afterwards in the admin by hand or with an app. That gap is in the file format, not in your spreadsheet.
Related reading
- The Shopify product CSV import guide
- Fixing the Shopify 25 MP image limit error
- How to bulk import products into Shopify
- Bulk assigning variant images after an import
- Why Shopify allows one image per variant
Fix the import, then fix the gallery
Clearing the errors gets your data into Shopify. It does not get the right photo in front of the shopper who clicked Blue, because the format cannot express that. If your catalogue arrived by CSV, that second job is still open.