If you have ever fed an FCS file into flowCore and been stopped cold by Error: $SPILLOVER size discrepancy, the good news is that your data is almost certainly fine. The problem is one malformed metadata keyword — and it is a known, fixable issue, most often coming from a BD FACSDiva export.
What the error actually means
flowCore — the R/Bioconductor package that most cytometry pipelines are built on — reads the compensation matrix from a keyword in the FCS file's TEXT segment called $SPILLOVER (or $SPILL in FCS 3.1+). When it parses that keyword, it counts the values. If the count does not match what the keyword says it should contain, flowCore raises a size discrepancy and refuses to read the file.
In other words: the error is not about your event data at all. The measurements are intact. It is purely a bookkeeping mismatch inside one metadata string.
The $SPILLOVER format, in one line
The compensation matrix is stored as a single comma-separated string with a very specific shape:
That is:
- a leading integer
n— the number of channels in the matrix; - then exactly
nchannel names (each must match a$PnNdetector name in the file); - then exactly
n × nmatrix values, in row-major order.
So a valid $SPILLOVER always contains exactly 1 + n + n² comma-separated tokens. A 3-channel matrix has 1 + 3 + 9 = 13 tokens. When that count is off — even by one — flowCore throws the size discrepancy.
Why BD FACSDiva produces malformed matrices
The single most common source of this error is a compensation matrix that was edited by hand in BD FACSDiva. When a user manually adjusts compensation and the workspace is exported, the written $SPILLOVER string can end up with a declared count n that no longer matches the number of names or values actually present. A frequent culprit is a stray trailing comma, which adds an empty token and pushes the total past 1 + n + n².
The matrix is usually still readable by a human — the numbers are all there. It is just that the self-describing count at the front is wrong, and flowCore validates that count strictly. FlowJo, by contrast, is tolerant: it quietly ignores or repairs the bad keyword and writes a clean one the next time you save the workspace. That difference is exactly why a file can open perfectly in FlowJo and fail instantly in R.
The short version: a strict reader (flowCore) counts the tokens and finds the count doesn't match the declared size. A tolerant reader (FlowJo) shrugs and moves on. Your data is fine either way.
Step 1 — Diagnose the exact malformation
Before fixing anything, it helps to see precisely what is wrong: how many tokens the keyword declares versus how many it actually contains. You can do this without installing anything and without uploading your file anywhere.
The free FlowVision FCS Validator runs entirely in your browser. Drop the file in and it parses the $SPILLOVER keyword the same way flowCore does, then reports the declared size, the expected token count, the actual token count, and — when it can — a corrected matrix string you can copy. If the discrepancy is a simple trailing-comma or off-by-one count, it will show you the fix directly.
Diagnose your file in the browser — no upload, no account.
Open the free FCS Validator →Step 2 — Three ways to fix it
Option A — Round-trip through FlowJo
The classic workaround: open the file (or workspace) in FlowJo and re-save it. Because FlowJo repairs the keyword on save, the re-exported file carries a clean $SPILLOVER that flowCore will accept. This works, but it requires a FlowJo licence — which is the whole reason many people are looking for an alternative in the first place.
Option B — Paste the corrected matrix string
If the malformation is a simple count mismatch (the most common case), the FCS Validator above will give you a corrected $SPILLOVER string with the right leading count and exactly n × n values. You can use that to overwrite the keyword in your analysis pipeline, or to hand-verify what the matrix should be. Always sanity-check the matrix values before relying on them — the validator re-counts tokens, it does not re-derive your compensation.
Option C — Strip or replace the keyword in R
If you compensate downstream anyway (for example, you apply your own spillover matrix), you can read the file with a tolerant parser and drop the offending keyword. A common pattern is to read the raw file, remove or overwrite the SPILLOVER / SPILL keyword, and then construct the compensation explicitly from a matrix you trust rather than the one embedded in the file.
This is the most robust long-term option if you process many files programmatically: never depend on the embedded matrix being well-formed — supply your own.
Preventing it next time
- Validate before batch runs. If you are about to process a folder of files, check a representative file (or each one) for keyword integrity first — a single malformed
$SPILLOVERcan halt an entire pipeline halfway through. - Be careful with manual compensation edits. Hand-editing the matrix in acquisition software is the most common way to introduce the mismatch. Re-export cleanly afterwards.
- Standardise on a trusted matrix. For programmatic pipelines, supply your own compensation matrix rather than relying on whatever each instrument wrote into the file.
None of this requires expensive software. The error looks alarming, but it is a one-keyword bookkeeping issue with a clear fix — and you can see exactly what is wrong with your file in a few seconds.
Want to analyze the file, not just fix it?
FlowVision desktop opens FACSDiva, Cytek, Sony and Beckman Coulter files, handles compensation and spectral unmixing, and exports back to FlowJo (.wsp) and Gating-ML — no lock-in. Windows + macOS, $99 lifetime.
Download the free trial →Related: FCS Validator · FCS Viewer · FlowVision vs FlowJo · All free FCS tools