Bulk Import validates every row in your CSV before anything is committed to your ledger. If even one row fails a check, the entire upload is held — none of the rows are created, including the good ones.
This article is the checklist of what the validator currently looks for, so you know what to fix and where.
If you haven't seen the validation step in action yet, Uploading and reviewing your CSV walks through the preview screen where these flags appear.
How errors show up in the preview
When the validator finds a problem on a row, two things happen on the preview screen:
An amber banner appears across the top of the preview: "Fix all errors before importing. Invalid rows are highlighted below."
The row is highlighted, and its Status column reads "Invalid": Valid rows continue to show "OK" alongside the bad ones.
The Status column currently shows just Invalid — there's no per-row tooltip or message explaining which check failed. To pinpoint the cause, run through the categories below against the highlighted row in your spreadsheet.
The # column in the preview matches the row number in your CSV, so a flagged row is easy to find.
The Import button at the bottom of the preview reads Import 0 transactions and is inactive anytime at least one row is invalid.
This is the all-or-nothing safety net. See Bulk import overview for the longer explanation of why.
What the validator currently checks
The list below covers everything the validator flags today. A few checks apply to every row (date, price, quantity, product matching, length limits); a few more apply only to rows that describe a custom product inline (metal, form, purity are only validated when you fill them in).
Date is missing, malformed, or in the future
Missing: The date column is required on every row. An empty cell flags the row.
Malformed: Dates must parse as a date. The template documents YYYY-MM-DD (e.g. 2024-07-15) and that's the format we test against. If a row's date doesn't parse, it flags.
Future date: Dates after today aren't allowed. Recordkeeping is about what's happened, not what you plan to buy.
Fix: Open the row in your spreadsheet, reformat the date cell to YYYY-MM-DD, and confirm the value is on or before today.
price_per_unit is missing
Missing: The price_per_unit column is required on every row. An empty cell flags the row.
Fix: Fill in what you paid per coin or bar, in USD. The value is per unit, not the all-in total for the row — if you bought 5 silver Eagles for $190, the right number is 38, not 190.
quantity is negative
Negative number: The quantity column can't accept negative values. Anything below zero flags the row.
Fix: Set quantity to the actual number of units bought as a positive whole number. If you're trying to record a sale, you're in the wrong tool — Bulk Import handles BUY transactions only. Use the Record Sale form instead; see How to record a sale.
Product matching is incomplete or wrong
Every row has to identify exactly one product. There are two valid shapes — the row needs to match one of them.
Path A — catalog_id is filled in. The key must match either a standard catalog slug (from the Standard Bullion tab of the Catalog page) or one of your saved custom items (the CSV Key on the My Custom Items tab). A typo or a key that doesn't exist flags the row.
Path B — catalog_id is blank. The row must include all five of product_name, metal, form, weight_oz, and purity. Any of those left blank flags the row.
Fix: Either correct the catalog_id (re-copy it from the Catalog page rather than typing it from memory) or fill in the complete set of five custom fields and leave catalog_id blank. For the full column reference, see Preparing your CSV.
metal or form value isn't a recognized option
Only applies on rows that use the no-catalog-id path. When metal and form are filled in, they're matched against a fixed set of values.
metal: Must be one of GOLD, SILVER, PLATINUM, PALLADIUM. Anything else flags the row.
form: Must be one of COIN, BAR, ROUND, JUNK. Anything else flags the row.
Casing is forgiving — gold works as well as GOLD, and coin as well as COIN — so the most common cause of a flag here is a typo (SILVR, BARR) or an unsupported value (MEDALLION, INGOT) rather than capitalization.
Fix: Correct the cell to one of the supported values. If your product doesn't fit any of the four form options, the closest match is usually ROUND for non-coin, non-bar pieces.
purity is out of range
Only applies on rows that use the no-catalog-id path. When purity is filled in, the value must be strictly greater than 0 and strictly less than 1.
Zero, negative, or 1 or higher: Flags the row. The most common slip is entering purity as a percentage — 99.99 instead of 0.9999 — which fails because the value is above 1.
Non-numeric: Anything that can't be read as a number flags the row.
Fix: Enter purity as a decimal fraction. Common values: 0.9999 for fine gold, 0.999 for fine silver, 0.917 for pre-1965 US 90% silver coins, 0.9 for British sovereigns, 0.5 for 50% silver coinage.
A field exceeds its length limit
Four columns have a maximum character count:
catalog_id: 200 characters.
product_name: 200 characters.
dealer: 200 characters.
notes: 2,000 characters.
In practice, you'll only ever hit these on notes, and only if you've pasted in a small essay. If you do, the cleanest fix is to trim the note to the essentials and move the longer write-up somewhere outside the ledger.
Fix: Shorten the offending cell below the cap.
CSV-level errors (missing required headers)
The four checks above run row by row, but there's one error that lives at the file level: the required header columns must exist in the CSV at all. If they're missing — usually because someone has deleted columns from the template or built a CSV from scratch with the wrong header names — the import surfaces an error message naming the missing headers rather than opening the preview table.
The required headers are the ones the validator checks per row: date, price_per_unit, and the product-matching set (catalog_id, product_name, metal, form, weight_oz, purity).
The optional columns (quantity, premium_per_unit, spot_at_time, shipping_cost, dealer, notes) don't need to be present as headers if you don't have data for them — but the simplest path is to keep the template's header row intact and leave the optional cells blank.
Fix: Re-download the template from the Bulk Import page, paste your data into the existing header structure, and re-upload.
A note on misspelled or extra columns
The import matches columns by header name, not position, and any column whose name isn't part of the template is silently ignored. There's a gotcha hiding in that behavior.
Misspelled optional headers vanish without warning. If you save a header as note instead of notes, or dealer_name instead of dealer, the column's data doesn't make it into the import — but the rows pass validation and import as if those fields were simply blank. Spell-check the headers if a field you expected to land in your ledger comes through empty.
Misspelled required headers do produce an error. If you save the header as dat instead of date, the import reports the missing required header and fails the file-level check above. So required headers fail loudly; optional headers fail quietly.
Extra columns are harmless. If you've added your own columns to the spreadsheet for personal bookkeeping (an order ID, a tracking number, a reminder note to yourself), they don't break anything — the import reads only the columns it knows about and ignores the rest.
If a row imported but a field is missing from the resulting transaction, the misspelled-header case is the first thing to check.
Re-uploading after a fix
Once you've corrected your CSV in the spreadsheet:
Save the file in CSV format. If you've been editing in Excel, the menu item is Save As → CSV (Comma delimited).
Click Replace in the top-right of the preview to swap in the corrected file. The page parses the new upload from scratch and re-validates every row.
Confirm every row reads OK in the Status column, then click Import N transactions to commit.
You don't need to re-do the upload from the empty state — Replace is the supported way to swap one CSV for another without losing your place.
Where to go next
Uploading and reviewing your CSV: The walkthrough of the preview screen where these flags appear.
Preparing your CSV: The column-by-column reference for what each field expects.
Bulk import overview: The conceptual tour, including why the import is all-or-nothing.
Bulk upload and your transaction limit: Once your rows are clean, the other thing that can stop an import — your plan's transaction cap.
My bulk upload failed: Troubleshooting from the other angle, for cases where you're not sure whether anything imported at all.
