# How to Fix Common DESeq2 Errors: A Troubleshooting Guide

Most DESeq2 errors trace back to one of three things: the count matrix is not what DESeq2 expects, the sample metadata does not line up with the matrix columns, or the design formula describes a comparison the data cannot support. DESeq2 is a negative binomial model built for raw integer counts from biological replicates, and it remains one of the most thoroughly benchmarked differential expression tools in R for Bioconductor workflows, with evaluations showing it performs at or near the top of the field once sample sizes reach six per group or more [1]. When it fails, the error message usually names the structural assumption that broke, which is exactly what DESeq2 error troubleshooting should exploit.

The single most common failure in real pipelines is a count matrix that contains non-integer values, typically because transcript-level abundance estimates from Salmon, Kallisto, or RSEM were loaded directly into `DESeqDataSetFromMatrix()`. If your run fails immediately after you pass a matrix to `DESeqDataSetFromMatrix()`, look at the values in that matrix first, before touching any other part of the script. Everything in this guide builds from that starting point and works outward toward rarer failures.

## The First Five Minutes: What to Check Before Anything Else

![Steps for the opening DESeq2 checks: versions, matrix class, integer test, sample name match and design matrix.](/article-images/illustrations/deseq2-error-troubleshooting-first-five-minutes.webp)

Before you touch any fix, run this short block and read its output carefully. In R for bioinformatics work, four out of five DESeq2 errors are visible in this output.

```r
library(DESeq2)
sessionInfo()                                  # record versions

class(cts)                                     # must be matrix-like, not data frame of characters
dim(cts)                                       # rows = genes, columns = samples
class(cts) <- "numeric"                        # reveal masked values
any(is.na(cts))                                # missing values break everything downstream
all(cts == round(cts))                         # FALSE means non-integer counts
range(cts)                                     # negative values should never appear
identical(colnames(cts), rownames(coldata))    # FALSE means sample mismatch
str(coldata)                                   # design variables must be factors or integers
model.matrix(design_formula, data = coldata)   # will error here if rank is deficient
```

Each line tests one structural assumption of the `DESeqDataSet` class. If `all(cts == round(cts))` returns `FALSE`, you have found the problem in most modern pipelines. If `identical(colnames(cts), rownames(coldata))` returns `FALSE`, you have found the second most common problem. If `model.matrix()` throws an error, your design formula is the culprit. Save this output in a log file, because Bioconductor tutorials and support forums will ask for your `sessionInfo()` output the moment you report a bug.

## Quick Reference: Symptoms, Causes, and Fixes

The table below is the core of this DESeq2 error troubleshooting guide. Work from the top down, because the most frequent causes sit at the top.

| Symptom (error text) | Likely cause | How to confirm | Fix |
|--|--|--|--|
| `some values in assay are not integers` | Salmon, Kallisto, RSEM, or estimated counts passed directly | `all(cts == round(cts))` is `FALSE` | Import with `tximport()` using `countsFromAbundance = "dtuScaledTPM"` or `"summaryCounts"`, or apply `countsFromAbundance` settings appropriate to your quantifier |
| `all variables in design formula must be columns in colData` | Typo in the formula, or variable stored in a separate object | `str(coldata)` and compare names to the formula | Add the variable to `coldata` or fix the spelling |
| `the model matrix is not full rank` | Design variables are confounded, or one sample per combination | `qr(model.matrix(...))$rank` is less than number of columns | Simplify the design, add replicates, or use interaction-aware grouping |
| `every gene contains at least one zero, cannot compute log geometric means` | Median-of-ratios size factors need geometric means, which fail with zeros | `rowSums(cts == 0) > 0` for all genes | Use `estimateSizeFactors(type = "poscounts")` or switch transformations from `rlog()` to `vst()` |
| `all genes have equal values` during size factor estimation | Count matrix has almost no variation, or genes were filtered incorrectly | `head(cts)` and check row sums | Check the gene filter step, re-import the raw matrix, verify sample columns |
| `size factors should be positive` | A sample column is all zeros | `colSums(cts)` contains zero | Remove the empty sample or investigate upstream mapping failure |
| `contrast requires a list of at least 2 elements` | `contrast` argument given one string or wrong type | Print the `resultsNames(dds)` vector | Use the correct `contrast = c("condition", "B", "A")` form or `name =` for a single coefficient |
| `coef not in names of mle` during `lfcShrink(type = "apeglm")` | Requested coefficient does not exist, or interaction term used with apeglm | Compare `resultsNames(dds)` to the `coef` argument | Use `contrast =` with `type = "ashr"` for interaction terms |
| `step size failed to converge` or iteration warnings | Hard-to-fit model, sparse counts, or too few replicates | Check `mcols(dds)$alpha` for extreme values | Increase `maxit` in `results()` or `DESeq()`, or simplify the design |
| `vector memory exhausted (limit reached?)` | R hit its allocation ceiling, common on macOS | Track memory with `pryr::mem_used()` | Restart the session, prefilter low-count genes, run steps one at a time |
| `cannot allocate vector of size ...` | Insufficient RAM for the dataset size | Compare error size to machine RAM | Filter genes before transformation, use `BiocParallel` carefully, work on a server |
| `object of type 'S4' is not subsettable` | Indexing a `DESeqDataSet` or `SummarizedExperiment` with base R syntax | Print the object class | Use `assay()`, `colData()`, or `counts(dds)` before indexing |

## Error Category 1: Input and Data Structure Problems

These are the most frequent DESeq2 error troubleshooting causes because they happen at the boundary between upstream quantification and [statistical modeling](/blog/guides/understanding-model-assumptions-in-statistical-analysis), where formats change hands.

### "Some values in assay are not integers"

DESeq2 models counts with the [negative binomial distribution](/blog/data-analysis/negative-binomial-distribution-formula-examples), which applies to whole numbers of mapped reads. The error fires the moment `DESeqDataSetFromMatrix()` detects a fractional value. Alignment and counting tools such as featureCounts, HTSeq, or Subread produce integers, so this error almost always means the counts came from a transcript quantifier. Salmon and Kallisto output estimated transcript abundances, and their per-transcript "counts" are expectations from a probabilistic assignment, so they are rarely whole numbers.

The correct fix is not `round()`. Rounding estimated counts discards information and changes the variance structure. The standard solution is the `tximport` approach, which sums transcript-level estimates to gene level and produces a matrix DESeq2 accepts. In Python environments, pytximport provides the same gene-level summarization, bias correction, and inferential replicate handling for Python-centric workflows built on the scverse ecosystem [2]. The Soybean Expression Atlas project demonstrates the same principle at database scale, using bias-corrected gene-level counts specifically to increase the accuracy of downstream differential expression analyses across thousands of samples [3].

If rounding is unavoidable, for example with legacy data, document it. A rounded matrix will run, but reviewers may ask why.

### Sample Names That Do Not Match Between Counts and colData

`DESeqDataSetFromMatrix(countData = cts, colData = coldata, design = ~ condition)` matches columns to rows by name. If a column is named `S1_R1.fastq.gz` in the matrix but `S1_R1` in the metadata table, or if the two objects have different column orders, DESeq2 stops with a mismatch error or silently produces a misaligned design. Misalignment is the dangerous case, because the model can fit without complaint and attach the wrong condition to the wrong sample.

Confirm alignment explicitly and force the order yourself:

```r
identical(colnames(cts), rownames(coldata))
cts <- cts[, rownames(coldata)]                # reorder counts to match metadata
```

The second line turns an optional safety check into a guarantee. Make it a permanent line in every script.

### Using TPM, FPKM, or CPM as Input

A recurring question in R for bioinformatics forums is whether normalized values can serve as DESeq2 input. They cannot. TPM and FPKM remove the library size information that DESeq2's size factor estimation depends on, and their non-integer values trigger the integer error anyway. Feed DESeq2 raw integer counts and let the package normalize. If your only available matrix is already normalized, go back to the raw counts from the original quantification step. If raw counts are genuinely unavailable, DESeq2 is the wrong tool for that dataset and an alternative framework should be considered.

### Transcript-Level Counts and Gene-Level Summarization

Transcript quantifiers now dominate modern pipelines, so the integer error increasingly appears at the start rather than the end of the analysis. The general pattern is:

```r
library(tximport)
files <- paste0(dir, "/", sample_table$sample_id, "/quant.sf")
names(files) <- sample_table$sample_id
txi <- tximport(files, type = "salmon", tx2gene = tx2gene,
                countsFromAbundance = "dtuScaledTPM")
dds <- DESeqDataSetFromTximport(txi, colData = sample_table, design = ~ condition)
```

The `DESeqDataSetFromTximport()` wrapper exists precisely to carry the correct offset and scaling into the model. Skipping it and pulling `txi$counts` directly into `DESeqDataSetFromMatrix()` is a real mistake readers make, and it changes the statistical interpretation because the effective transcript lengths no longer enter the model.

## Error Category 2: Design Formula and Model Matrix Errors

### "The Model Matrix Is Not Full Rank"

This is the conceptually hardest error for newcomers, and it is worth understanding the mechanism rather than memorizing the fix. A linear model needs each coefficient to be identifiable from the data. If two design variables move together perfectly, for example every treated animal was processed on day 2 and every control on day 1, the model cannot separate treatment from batch, and the matrix loses rank. DESeq2 detects this before fitting anything and stops.

The most common concrete triggers:

1. **Perfect confounding.** Sex, batch, site, or treatment perfectly aligned. The fix is experimental design, not code. If you cannot add samples, you must drop one of the confounded terms and acknowledge the limitation.
2. **One sample per level combination.** A design like `~ patient + treatment` where each patient contributes exactly one sample is not full rank for `treatment`. Add a replicate, or use a design such as `~ patient + condition` only when at least one patient has both conditions.
3. **Paired or blocked designs handled wrong.** For paired data, the blocking variable goes in the formula and the pairing variable must have levels that repeat across conditions.
4. **Numeric variables that should be factors.** A batch stored as 1, 2, 3 will be treated as a continuous trend. Convert with `coldata$batch <- factor(coldata$batch)`.

Test the rank directly before running DESeq2:

```r
mm <- model.matrix(~ batch + condition, data = coldata)
qr(mm)$rank == ncol(mm)                        # TRUE means full rank
```

### Variables Missing From colData

The error `all variables in design formula must be columns in colData` is self-describing. It almost always comes from a spelling difference such as `treatment` versus `Treatment`, or from variables living in a global object rather than in `coldata`. Build the metadata table once, from a sample sheet file, and never type sample information by hand into the script.

### Reference Levels and Factor Ordering

DESeq2 names results coefficients after the factor level order, and the default alphabetical order often picks an unhelpful reference group. Setting the reference level explicitly prevents both wrong-comparison errors and the confusing `conditionBvsA` naming that appears when you expected the opposite:

```r
coldata$condition <- factor(coldata$condition, levels = c("control", "treated"))
```

A related real mistake is leaving condition as character. DESeq2 will convert characters to factors with a warning, but the automatic ordering may not match your intent. Convert factors deliberately, at the top of the script, where the choice is visible.

### Interaction Terms and Contrast Errors

The error `contrast requires a list of at least 2 elements` appears when `results()` receives a single string where it expected the three-element form, or when a two-element contrast list is needed for interaction terms. For a model with an interaction such as `~ genotype + treatment + genotype:treatment`, the contrast for the treatment effect in one genotype is a list:

```r
results(dds, contrast = list(c("treatmentB", "genotypeKO.treatmentB")))
```

Interaction contrasts are the most error-prone part of the interface. Always print `resultsNames(dds)` first and build the contrast from that vector, never from memory.

## Error Category 3: Size Factor and Normalization Errors

Size factors are DESeq2's estimate of each sample's sequencing depth and composition, computed by the median-of-ratios method. Several classic errors originate here.

### "Every Gene Contains at Least One Zero, Cannot Compute Log Geometric Means"

The median-of-ratios method needs a per-gene geometric mean across samples, and a single zero in any sample makes that mean undefined. Datasets with many sparse genes, such as single-cell pseudobulk with low depth or organisms with large gene families, can hit a state where every gene has at least one zero. The documented fix is the positive counts variant:

```r
dds <- estimateSizeFactors(dds, type = "poscounts")
```

For visualization transformations, `vst()` handles zeros gracefully and has largely replaced `rlog()` for large datasets, so switching from `rlog()` to `vst()` resolves the same underlying issue without changing the size factor type.

### "All Genes Have Equal Values"

When size factor estimation reports equal gene means, the count matrix itself is usually wrong. Check, in order: whether the gene filtering step accidentally removed all variable genes, whether samples were permuted during a merge, and whether the matrix actually contains the raw counts rather than a transformed copy. In one documented pipeline failure mode, filtering rules applied to the wrong axis of the matrix strip all biological variation before modeling even starts. Re-import the raw matrix and re-apply the filter step by step, checking `summary(rowSums(cts))` after each operation.

### Residual Library Size Effects

A related and subtler issue is that normalized counts and differential results can remain correlated with library size even after standard correction, an effect documented across more than 100 public GEO and TCGA datasets and addressed by gene-specific normalization approaches such as gecco [4]. This rarely produces a hard error, but it belongs in DESeq2 error troubleshooting because it explains results that look wrong without any error message, particularly when library sizes span a wide range. Checking `plot(counts(dds, normalized = TRUE)[gene, ] ~ sizeFactors(dds))` for genes that should not track depth is a practical diagnostic.

## Error Category 4: Model Fitting and Convergence Errors

### Convergence Failures in the Wald Test

Warnings such as `step size failed to converge` or `iteration limit reached` mean the iteratively reweighted least squares fit for one or more genes did not settle within the maximum iterations. Sparse counts with few replicates are the usual cause. The practical fixes, in order of preference, are to simplify the design formula, increase iterations with `DESeq(dds, fitType = "local")` or higher `maxit` in `results()`, or accept the flagged genes and check `mcols(dds)$maxCooks`. These warnings are not fatal. Genes that fail to converge simply carry reduced confidence, and the `results()` call still completes.

### Outliers and Cook's Distance

DESeq2 flags genes where one sample shows an extreme count inconsistent with the fitted model, using Cook's distance. With seven or more replicates in a group, flagged genes are replaced by a trimmed mean and refit automatically. With fewer replicates, DESeq2 cannot replace the outlier and the gene is simply flagged in `mcols(dds)$maxCooks`. Errors around this machinery usually stem from trying to force replacement with too few replicates. Confirm the replicate count per group and let the default behavior operate, or inspect flagged genes manually in a PCA plot to see whether the outlying sample warrants exclusion on technical grounds.

### Extremely Skewed Count Distributions

Some data types strain the negative binomial model itself. miRNA-seq counts, which range from zero to millions and cluster at extremes, are a documented case where Poisson and negative binomial fits degrade, and heavy-tailed discrete stable models have been proposed specifically for that data type [5]. When troubleshooting repeated convergence warnings on miRNA or other highly skewed data, the model itself may be the limiting factor rather than any code error, and a method designed for the distribution at hand deserves consideration.

## Error Category 5: Transformation and Shrinkage Errors

### rlog and vst Failures

`rlog()` inherits the geometric mean problem described above and can fail on sparse matrices with the `every gene contains at least one zero` message. `vst()` computes quickly and tolerates zeros, so the fix is straightforward: use `vst(dds)` for exploratory plots, reserving `rlog()` for small, dense datasets where its extra runtime buys slightly better dynamic range control. A separate failure mode is passing a `DESeqTransform` object to functions expecting a `DESeqDataSet`, which produces cryptic S4 class errors. Check the class with `class(vsd)` and extract the matrix with `assay(vsd)` before any downstream plotting.

### lfcShrink: coef Versus Contrast

Two separate shrinkage errors account for most log fold change shrinkage problems. First, `type = "apeglm"` shrinks a single coefficient, so requesting it on an interaction term fails. The fix is `type = "ashr"` with a `contrast` argument, which handles arbitrary contrasts. Second, the coefficient name must match `resultsNames(dds)` exactly, including suffixes like `conditiontreatedvscontrol`. The reliable pattern is:

```r
resultsNames(dds)                              # copy the exact name from here
res <- lfcShrink(dds, coef = "conditiontreatedvscontrol", type = "apeglm")
```

## Error Category 6: Memory and Performance Problems

### "Vector Memory Exhausted" and Allocation Failures

The `vector memory exhausted (limit reached?)` message appears frequently on macOS, where R inherits a memory ceiling. `cannot allocate vector of size X` indicates genuine RAM limits. Both share the same fixes. Prefilter low-count genes before transformations, because a matrix with 60,000 genes and 50 samples already consumes substantial memory, and transformations like `rlog()` hold several copies. Restart the R session, load only what the current step needs, and run the workflow in stages saved to disk with `saveRDS()`. For large projects, standard practice is to store the `DESeqDataSet` object and intermediate results as RDS files so a memory failure does not force a full rerun.

### Slow Runs

Wald testing on tens of thousands of genes can take many minutes on a laptop. The `BiocParallel` argument in `DESeq()` distributes gene-wise fits across cores. Set `BiocParallel::register(MulticoreParam(4))` and confirm cores are actually free before starting. One caution: parallel workers multiply memory use, so a machine that barely fits the serial run will fail in parallel mode.

## Error Category 7: Installation and Environment Errors

Many reported DESeq2 failures are not model errors at all. A recent end-to-end RNA-seq protocol notes that general RNA-seq troubleshooting centers on configuring essential tools, resolving path and dependency issues, and ensuring proper handling of paired-end reads during upstream processing [6]. The same applies at the R level. Bioconductor packages are versioned against specific R releases, so a DESeq2 install that fails with dependency errors usually means the R version and Bioconductor release are out of sync. The fix is to upgrade both together:

```r
if (!requireNamespace("BiocManager", quietly = TRUE))
    install.packages("BiocManager")
BiocManager::install()
BiocManager::install("DESeq2")
```

After any install, run `packageVersion("DESeq2")` and `sessionInfo()` and record them in your log. Two analysts running different DESeq2 versions can get different default behaviors, which is a reproducibility problem that looks like a data problem. A short note on shared systems: store and transfer RNA-seq datasets from human or animal subjects under your institution's data governance rules, and never place controlled-access data in shared or unencrypted compute environments.

## DESeq2 Error Troubleshooting in Practice: A Diagnostic Sequence

When an error appears and the table above does not resolve it immediately, follow this sequence from top to bottom. Each step either resolves a class of errors or rules it out, so the sequence converges even on unfamiliar failures.

1. **Read the last error, not the first.** R prints the traceback from the bottom, and the final line names the failing assumption.
2. **Confirm versions.** `sessionInfo()`. Version mismatches explain install and behavior errors instantly.
3. **Verify the count matrix.** Class, dimensions, integer check, NA check, range check, as in the opening block.
4. **Verify sample alignment.** Column names against `rownames(coldata)`, then force the order.
5. **Verify the design.** All variables present as factors, `model.matrix()` succeeds, and `qr()$rank` equals the coefficient count.
6. **Run one step at a time.** Build the `DESeqDataSet`, check it with `colSums(counts(dds))`, estimate size factors, inspect `sizeFactors(dds)`, then run `DESeq()`. The step that fails names the problem.
7. **Inspect intermediate objects.** `head(counts(dds))`, `sizeFactors(dds)`, `resultsNames(dds)`. Most downstream errors are upstream surprises.
8. **Search the exact error string.** The Bioconductor support site archives nearly every DESeq2 error with solutions, and searching the verbatim message beats searching symptoms.

This sequence reflects how experienced analysts in R for bioinformatics work actually debug, and it doubles as documentation if you record the output at each step.

## Prevention Practices That Stop Errors Before They Start

### Freeze the Sample Sheet

Create one tab-separated or CSV sample sheet containing sample ID, file paths, and every design variable. Read it once with `read.csv()`, derive `coldata` from it, and never edit metadata inside the R script. Every mismatch and factor-ordering error in this guide becomes impossible when the sample sheet is the single source of truth.

### Check Inputs at the Boundary

Add three assertion lines before model building and let them fail loudly:

```r
stopifnot(all(cts == round(cts)),
          identical(colnames(cts), rownames(coldata)),
          all(!is.na(cts)))
```

This converts silent misalignment into an immediate, self-explanatory error, which is the cheapest debugging available in bioconductor tutorial workflows and production pipelines alike.

### Save and Version Intermediate Objects

`saveRDS(dds, "dds_v2.rds")` after each major stage, with the script version and `sessionInfo()` logged alongside. When an error appears, you can rerun the failing step against the saved object instead of the whole pipeline.

### Consider Structured Pipelines for Repeated Runs

Labs that run the same analysis repeatedly face these same errors on every new dataset, which is why automated pipelines have proliferated. SeqExpressionAnalyser wraps the full path from FASTQ files through quality control, alignment, counting, and differential expression in a single Shiny application, removing many of the format-boundary errors this guide covers [7]. RNA-SeqEZPZ provides a similar point-and-click or command-line pipeline spanning quality control through pathway analysis, with implementations in bash with SLURM and in Nextflow for portability across computing environments [8]. Structured pipelines are not a substitute for understanding the errors, but they enforce the input checks automatically.

## When DESeq2 Keeps Failing: Alternatives That Handle the Same Data

![Decision path asking about replicate count, fractional counts and single-cell pseudobulk to pick EBSeq, edgeR version 4 or RankCompV3.](/article-images/illustrations/deseq2-error-troubleshooting-which-alternative.webp)

Sometimes the error is not an error but a mismatch between the method and the data, and a different statistical framework is the correct solution. Benchmarking across eight common R and Bioconductor differential expression methods found that with three replicates per group, EBSeq performed best on false discovery rate control, power, and stability, while DESeq2 led when sample sizes reached six or twelve per group [1]. If your dataset has three replicates per condition and DESeq2 keeps producing convergence warnings or implausible results, that benchmark suggests the sample size, not your code, is the driving factor.

edgeR, the other major negative binomial package, handles the same count input and continues to expand, with version 4 adding support for fractional counts, a C-based model fitting implementation, and an improved quasi-likelihood treatment for small counts [9]. Fractional count support is directly relevant to the transcript-quantifier workflow described above, because it removes the integer requirement at the source. For datasets where clustering structure matters, MBCdeg3 identifies and classifies differentially expressed genes together and performed well across simulation scenarios when compared against edgeR, DESeq2, and TCC [10]. For analyses where mean-based testing misses biology, SIEVEseq provides a unified framework testing differential expression, variability, and skewness simultaneously, with simulation results showing improved false discovery rate control relative to existing methods [11]. For single-cell data analyzed as pseudobulk, RankCompV3 offers a relative expression ordering approach that tightly controlled false positive rates against eleven common single-cell differential expression algorithms [12].

A concrete illustration of DESeq2 working correctly in practice comes from a corneal wound healing study that used DESeq2 for differential expression between human corneal fibroblasts and myofibroblasts, identifying thousands of genes at adjusted P below 0.05 and then applying a log fold change threshold of at least 1.5 in either direction to prioritize targets [13]. That workflow shows the two-stage pattern worth copying: statistical significance from `results()`, biological prioritization from a fold change cutoff.

## Common Mistakes and Limitations

- **Rounding estimated counts instead of importing them properly.** `round()` makes the integer error disappear while degrading the statistical basis of the analysis. Use `tximport` or a package that accepts fractional counts.
- **Fixing rank errors by dropping replicates from the model.** When `not full rank` appears, the temptation is to delete a confounded variable. Sometimes that is right, but often it hides the confounding in the residual and produces overconfident p values. Document whatever you remove.
- **Sorting samples differently in the counts matrix and metadata.** Row and column matching by name must be verified, not assumed. One misalignment silently assigns the wrong treatment to a sample and invalidates the entire comparison.
- **Interpreting transformed values as counts.** After `vst()` or `rlog()`, the values are variance-stabilized, not counts. Feeding them back into DESeq2 or into methods expecting raw counts produces undefined behavior.
- **Chasing convergence warnings as errors.** Warnings from the Wald test identify difficult genes, not a broken pipeline. Fixing them by tweaking the design or filtering should be a deliberate choice, recorded in the methods.
- **Assuming every non-standard dataset fits the negative binomial.** The miRNA-seq case shows the model has distributional limits [5], and benchmarking shows no single method wins every scenario [1]. Match the method to the data.
- **Bulk-level errors that reflect biology.** Bulk RNA-seq averages across cell populations, and deconvolution studies show that inferred cell proportions track mRNA content more closely than true cell counts [14]. An inexplicable DESeq2 result sometimes reflects compositional change across samples rather than a code failure, and cell composition analysis is the appropriate next step.
- **No single run replaces review.** Individual datasets carry design choices, batch structure, and data quality issues that warrant review by an experienced analyst before results are trusted for publication or decision making.

## Frequently Asked Questions

### Why does DESeq2 say some values in my assay are not integers?

Because the matrix contains estimated counts from a transcript quantifier such as Salmon or Kallisto, which are fractional by design. Import them through `tximport()` with gene-level summarization, or use a tool like edgeR version 4 that accepts fractional counts directly.

### Why does my design matrix need to be full rank?

Each coefficient in the model must be estimable from the data. When two design variables are perfectly confounded, or when a combination of levels has only one sample, the coefficients become indistinguishable and DESeq2 refuses to fit rather than return meaningless results.

### Can I use TPM or FPKM values as DESeq2 input?

No. DESeq2 requires raw integer counts because its size factor estimation and negative binomial model depend on the library size information that TPM and FPKM have removed. Return to the raw counts from the quantification step.

### What does "every gene contains at least one zero, cannot compute log geometric means" mean?

The median-of-ratios size factor method needs a per-gene geometric mean across all samples, and any zero makes that mean undefined. Run `estimateSizeFactors(dds, type = "poscounts")` or switch visualization transformations from `rlog()` to `vst()`.

### How many replicates do I need for reliable DESeq2 results?

Benchmarking of eight differential expression methods found EBSeq strongest with three replicates per group, while DESeq2 performed best when groups reached six or twelve replicates. More replicates also reduce convergence warnings and make outlier handling automatic.

### What is the difference between coef and contrast in lfcShrink?

The `coef` argument shrinks one named coefficient from `resultsNames(dds)` and works with `type = "apeglm"`. The `contrast` argument handles arbitrary comparisons, including interaction terms, and requires `type = "ashr"`. Using apeglm on an interaction term is a common error.

### How do I fix the memory exhausted error in DESeq2?

Restart the R session, prefilter low-count genes, and save intermediate objects with `saveRDS()` so steps run in stages. On macOS the error often reflects a memory ceiling rather than actual RAM limits, and running on a server resolves persistent cases.

### When should I use an alternative to DESeq2?

Consider alternatives when the data violates the model assumptions or errors persist after correct input handling. Three-replicate datasets may do better with EBSeq, fractional transcript counts run natively in edgeR version 4, and unified frameworks such as SIEVEseq extend testing to variability and skewness beyond mean-based differential expression.

<script type="application/ld+json">
{
  "@context": "https://schema.org",
  "@type": "FAQPage",
  "mainEntity": [
    {
      "@type": "Question",
      "name": "Why does DESeq2 say some values in my assay are not integers?",
      "acceptedAnswer": {
        "@type": "Answer",
        "text": "Because the matrix contains estimated counts from a transcript quantifier such as Salmon or Kallisto, which are fractional by design. Import them through tximport with gene-level summarization, or use a tool like edgeR version 4 that accepts fractional counts directly."
      }
    },
    {
      "@type": "Question",
      "name": "Why does my design matrix need to be full rank?",
      "acceptedAnswer": {
        "@type": "Answer",
        "text": "Each coefficient in the model must be estimable from the data. When two design variables are perfectly confounded, or when a combination of levels has only one sample, the coefficients become indistinguishable and DESeq2 refuses to fit rather than return meaningless results."
      }
    },
    {
      "@type": "Question",
      "name": "Can I use TPM or FPKM values as DESeq2 input?",
      "acceptedAnswer": {
        "@type": "Answer",
        "text": "No. DESeq2 requires raw integer counts because its size factor estimation and negative binomial model depend on the library size information that TPM and FPKM have removed. Return to the raw counts from the quantification step."
      }
    },
    {
      "@type": "Question",
      "name": "What does the error about log geometric means mean?",
      "acceptedAnswer": {
        "@type": "Answer",
        "text": "The median-of-ratios size factor method needs a per-gene [geometric mean](/blog/data-analysis/geometric-mean-definition-formula) across all samples, and any zero makes that mean undefined. Run estimateSizeFactors with type set to poscounts, or switch visualization transformations from rlog to vst."
      }
    },
    {
      "@type": "Question",
      "name": "How many replicates do I need for reliable DESeq2 results?",
      "acceptedAnswer": {
        "@type": "Answer",
        "text": "Benchmarking of eight differential expression methods found EBSeq strongest with three replicates per group, while DESeq2 performed best when groups reached six or twelve replicates. More replicates also reduce convergence warnings and make outlier handling automatic."
      }
    },
    {
      "@type": "Question",
      "name": "What is the difference between coef and contrast in lfcShrink?",
      "acceptedAnswer": {
        "@type": "Answer",
        "text": "The coef argument shrinks one named coefficient from resultsNames and works with type apeglm. The contrast argument handles arbitrary comparisons, including interaction terms, and requires type ashr. Using apeglm on an interaction term is a common error."
      }
    },
    {
      "@type": "Question",
      "name": "How do I fix the memory exhausted error in DESeq2?",
      "acceptedAnswer": {
        "@type": "Answer",
        "text": "Restart the R session, prefilter low-count genes, and save intermediate objects with saveRDS so steps run in stages. On macOS the error often reflects a memory ceiling rather than actual RAM limits, and running on a server resolves persistent cases."
      }
    },
    {
      "@type": "Question",
      "name": "When should I use an alternative to DESeq2?",
      "acceptedAnswer": {
        "@type": "Answer",
        "text": "Consider alternatives when the data violates the model assumptions or errors persist after correct input handling. Three-replicate datasets may do better with EBSeq, fractional transcript counts run natively in edgeR version 4, and unified frameworks such as SIEVEseq extend testing to variability and skewness beyond mean-based differential expression."
      }
    }
  ]
}
</script>

## Sources

1. [An evaluation of RNA-seq differential analysis methods.](https://pubmed.ncbi.nlm.nih.gov/36112652/)
2. [Gene count estimation with pytximport enables reproducible analysis of bulk RNA sequencing data in Python.](https://pubmed.ncbi.nlm.nih.gov/39565903/)
3. [The Soybean Expression Atlas v2: A comprehensive database of over 5000 RNA-seq samples.](https://pubmed.ncbi.nlm.nih.gov/37681739/)
4. [Gene-specific exponent-corrected normalization for library size in bulk RNA-seq.](https://pubmed.ncbi.nlm.nih.gov/42465367/)
5. [A heavy-tailed model for analyzing miRNA-seq raw read counts.](https://pubmed.ncbi.nlm.nih.gov/38810893/)
6. [Streamline Protocol for Bulk-RNA Sequencing: From Data Extraction to Expression Analysis.](https://pubmed.ncbi.nlm.nih.gov/41637157/)
7. [SeqExpressionAnalyser: An R Package for Automated End-to-End RNA-Seq Analysis From Reads to Differential Expression.](https://pubmed.ncbi.nlm.nih.gov/41602550/)
8. [RNA-SeqEZPZ: A Point-and-Click Pipeline for Comprehensive Transcriptomics Analysis with Interactive Visualizations.](https://pubmed.ncbi.nlm.nih.gov/39763815/)
9. [edgeR v4: powerful differential analysis of sequencing data with expanded functionality and improved support for small counts and larger datasets.](https://pubmed.ncbi.nlm.nih.gov/39844453/)
10. [Enhanced clustering-based differential expression analysis method for RNA-seq data.](https://pubmed.ncbi.nlm.nih.gov/38179066/)
11. [SIEVEseq: unified differential expression, variability, and skewness analyses using RNA-Seq data.](https://pubmed.ncbi.nlm.nih.gov/42522708/)
12. [RankCompV3: a differential expression analysis algorithm based on relative expression orderings and applications in single-cell RNA transcriptomics.](https://pubmed.ncbi.nlm.nih.gov/39112940/)
13. [RNA-Seq Analysis Unraveling Novel Genes and Pathways Influencing Corneal Wound Healing.](https://pubmed.ncbi.nlm.nih.gov/39240550/)
14. [Cell-Type Deconvolution of Equine BALF RNA-Seq: A Critical Comparison with Matched Single-Cell Data.](https://pubmed.ncbi.nlm.nih.gov/42510813/)