# How to Fix 'Dispersion Trend Is Flat' in DESeq2

The most common cause of the "dispersion trend is flat" message in DESeq2 is a dataset where the per-gene dispersion estimates do not rise as mean expression falls, so the fitted trend line collapses to a nearly constant curve. This happens most often in experiments with few genes left after filtering, very few replicates, or count data with an unusually weak mean-dispersion relationship. The warning does not stop the run, but it tells you that the fitted curve deserves a second look before you trust the p-values.

The first thing to check is simple. Run `plotDispEsts(dds)` immediately after `DESeq(dds)` finishes and compare the fitted trend (the red curve) against the gene-wise dispersion estimates (the black points). If the red curve tracks the cloud of black points well, even though it looks flat, the warning is usually harmless and you can proceed. If the red curve cuts across the point cloud, or if most points sit on the floor of the plot, you need one of the fixes described below.

This article explains what the warning means mechanistically, walks through the causes from most common to rarest, gives a step-by-step diagnostic sequence you can run in R, and closes with prevention practices and a troubleshooting table you can keep next to your session.

## What the Warning Actually Means

DESeq2 models RNA-seq count data with the [negative binomial distribution](/blog/data-analysis/negative-binomial-distribution-formula-examples). In that model, the variance of each gene's counts is described as the sum of a Poisson sampling term and an overdispersion term, and the overdispersion is controlled by a parameter called the dispersion, usually written as alpha. The relationship is:

```
variance = mean + alpha * mean^2
```

A dispersion near zero means the gene's counts are close to Poisson noise. A dispersion of 0.1 means the variance is roughly ten percent larger than the mean squared term alone would predict. DESeq2 needs a dispersion value for every gene to compute the uncertainty of fold changes and to run the Wald test or the likelihood ratio test.

There is one dispersion per gene, but there are usually far too few replicates to estimate each one accurately on its own. DESeq2 therefore follows a two-step strategy, described in the method's original documentation and companion papers. First it computes a rough gene-wise dispersion estimate for every gene using the counts within each group of the design. Second, it fits a smooth trend of dispersion against mean expression across all genes and shrinks each gene's estimate toward that trend. The shrunk value is what the statistical test actually uses. Genes with exceptionally high estimates in excess of the trend are kept at their gene-wise value so that true outliers are not hidden.

The trend fit is where the warning appears. With the default `fitType = "parametric"`, DESeq2 fits a curve of the form:

```
dispersion = a1 / mean + a0
```

This shape captures the expected biology. Low-count genes carry relatively more sampling noise, so their dispersion estimates tend to be high and the 1/mean term dominates. High-count genes have precise estimates, so the curve falls and levels off toward a floor value a0. The fit is performed on the subset of genes whose gene-wise estimates sit clearly above the numerical lower bound, because genes pinned at the floor carry no information about the shape of the curve.

The message "dispersion trend is flat" fires when the fitted curve has essentially no slope across the observed range of mean expression. In terms of the parametric form, the coefficient on the 1/mean term is close to zero relative to the intercept, so the fitted "curve" is almost a horizontal line. DESeq2 prints the warning and continues with the fit. The run completes. What the warning is telling you is that the standard expectation, higher dispersion at low counts declining smoothly toward a floor at high counts, did not show up in your data.

Whether that matters depends on why it happened. Sometimes the data genuinely have a flat mean-dispersion relationship and a horizontal line is the right fit. Sometimes the fit is flat because the gene-wise estimates feeding it are noisy or distorted, and then the shrunk dispersions used in testing are pulled toward a misleading target. Distinguishing these two situations is the job of the rest of this article.

## Start With plotDispEsts

![Decision path from the flat trend warning through the three plotDispEsts patterns to accepting the fit, changing fit type, or fixing the input matrix.](/article-images/illustrations/deseq2-dispersion-trend-flat-warning-triage.webp)

`plotDispEsts(dds)` is the single most informative diagnostic for this warning, and it costs one line of code:

```r
dds <- DESeq(dds)
plotDispEsts(dds)
```

The plot shows four kinds of elements on log-log axes:

1. **Black dots** are the per-gene dispersion estimates computed directly from the counts, before any shrinkage.
2. **The red curve** is the fitted mean-dispersion trend, the object of the warning.
3. **Blue dots** are the final shrunk dispersions that DESeq2 uses for testing.
4. **Black circles** mark genes flagged as count outliers by Cook's distance during testing.

With a healthy dataset, the black dots form a wedge that narrows from upper left to lower right, the red curve runs through the center of the wedge, and the blue dots sit between the black dots and the red curve. With a flat trend warning, you will typically see one of three patterns.

**Pattern A: a flat red curve that still runs through the middle of the point cloud.** The data simply lack a strong mean-dispersion relationship. The fit is describing your data faithfully and the warning is informational. Proceed with the results.

**Pattern B: most black dots piled at the bottom of the plot.** DESeq2 sets a small lower bound on dispersion estimates during fitting, on the order of 1e-8, so genes with almost no extra-Poisson variation pile up near the floor of the log-scale plot. The trend fit excludes those genes, and if few genes remain above the floor, the fit can degenerate. This pattern is common with UMI-processed data, spike-in-only tables, or heavily simulated or filtered matrices.

**Pattern C: the red curve clearly misses the black dots.** For example, the curve is horizontal while the dots still decline with mean, or a handful of extremely high-dispersion genes drag the fit away from the bulk. Here the trend is distorted and the shrunk dispersions will be biased, so you should intervene with one of the fixes below.

Capture the plot to a file so it lands in your project record alongside the code that produced it:

```r
png("plotDispEsts.png", width = 900, height = 700)
plotDispEsts(dds)
dev.off()
```

## Troubleshooting Table

The table below is the core reference for this article. Work from top to bottom, matching what you see in `plotDispEsts` and your session log to the symptom column.

| Symptom | Likely cause | How to confirm | Fix |
|, - |, - |, - |, - |
| Warning printed, red curve tracks the black dots, blue dots sit sensibly between them | Genuinely weak mean-dispersion relationship, benign | Visual match in `plotDispEsts`, results stable across fit types | Proceed, document the warning in your methods note |
| Most black dots sit near the bottom of the plot | Near-Poisson data or excess zero rows dominating the table | Many genes with baseMean near zero, dispersion estimates at the floor | Apply standard pre-filtering, reconsider `fitType = "mean"` |
| Only a few hundred genes or fewer in the table | Aggressive filtering, small genome, targeted panel, spike-ins only | `nrow(dds)` after filtering, review your filter thresholds | Relax the filter, or accept a local or mean fit and report it |
| Counts contain fractional values, or you loaded TPM, FPKM, or CPM values | Wrong input matrix, DESeq2 requires raw integer counts | `all(counts(dds) == round(counts(dds)))`, inspect your import script | Re-import raw gene-level counts, or use `tximport` for transcript quantifiers |
| Few samples per group, black dot cloud very wide and noisy | Low replicate number makes gene-wise estimates unstable | `colData(dds)` group sizes, compare with typical designs of 3 or more per group | Add replicates if possible, compare local and mean fits, interpret results conservatively |
| A handful of genes with enormous dispersion estimates far above the rest | Outlier genes or large count outliers dominate the robust fit | Black circles in `plotDispEsts`, `summary(results(dds))` outlier counts | Inspect flagged genes, rely on `minReplicatesForReplace` when replicates allow replacement |
| Flat trend plus size factors spanning an extreme range | Library size imbalance distorting gene-wise estimates | `summary(sizeFactors(dds))`, inspect the ratio of largest to smallest | Re-check normalization, investigate samples with extreme factors |
| Run stops with an error instead of a warning | Different problem, the parametric fit failed outright | Message text says the parametric dispersion fit failed | DESeq2 falls back to a local fit automatically, or set `fitType = "local"` yourself |

## Causes From Most Common to Rarest

### Cause 1: The mean-dispersion relationship is genuinely weak

This is the most frequent explanation and the one you should assume first, after confirming with the plot. Dispersion reflects how much a gene's counts vary between biological replicates beyond Poisson noise. In many real datasets, especially those with tight experimental control, consistent library preparation, or broad dynamic range, the overdispersion is similar across the whole expression range. The parametric model expects the 1/mean term to add dispersion at the low end, but if low-count genes are not actually noisier in relative terms, the fitted slope goes to nearly zero and DESeq2 warns you.

Confirm it by looking at Pattern A above and by verifying that the fit is not fighting the data. Then confirm it functionally by rerunning with `fitType = "local"`. The local fit uses a smoothed curve instead of a fixed parametric form and will reproduce a flat relationship as a flat relationship. If the number of differentially expressed genes and the top hits stay essentially the same between the two fits, the warning was cosmetic.

There is nothing to fix in this case. The correct action is to record that you saw the warning, checked the fit, compared fit types, and observed stable results. Reviewers and collaborators respond well to that one-line justification.

### Cause 2: Too few genes remain for a stable trend fit

The parametric fit needs enough genes above the dispersion floor to estimate two coefficients reliably. Datasets that routinely trigger the warning for this reason include:

- Microbial or viral RNA-seq where the genome encodes only a few thousand genes or fewer, and filtering removes a large share of them.
- Targeted panels or small custom gene sets with a few hundred features.
- Tables containing only spike-in controls.
- Standard eukaryotic datasets where a very strict filter was applied before dispersion estimation.

You can see the problem directly. DESeq2 excludes genes whose gene-wise estimate sits at the numerical floor from the trend fit, because a value pinned at the boundary tells the fit nothing about shape. If your table has 5,000 genes and 4,700 of them are at or near the floor, the parametric fit is being estimated from a few hundred points, many of which carry noisy gene-wise estimates. The optimizer can converge to a near-horizontal solution.

Confirm this by counting what the fit actually sees:

```r
sum(mcols(dds)$allZero)
nrow(dds)
summary(mcols(dds)$baseMean)
```

If the proportion of non-zero genes is small or the total gene number is in the low hundreds, the warning is likely a sample-size problem, not a biology problem. Two remedies exist. First, revisit the filtering. The DESeq2 documentation recommends a light pre-filter that removes genes with almost no counts, for example keeping genes with ten or more reads in at least three samples:

```r
keep <- rowSums(counts(dds) >= 10) >= 3
dds <- dds[keep, ]
```

Do not over-filter. Filtering more aggressively than needed to remove rows of near-total zeros shrinks the fit input further. Second, if the gene set is genuinely small, switch to `fitType = "local"` or `fitType = "mean"`. With few points, a rigid parametric curve is fragile, while the mean fit, which uses a single constant dispersion equal to the mean of the gene-wise estimates, can be the most stable option of all.

### Cause 3: Near-Poisson or zero-heavy count structure

A flat trend can also reflect the internal shape of the count matrix. Two related situations produce it.

The first is data with very low overdispersion everywhere. Counts from well-behaved UMI-based protocols, or from sources where technical variance was minimized, can sit close to the Poisson limit across the full expression range. Every gene-wise estimate lands near the floor, the fit has almost nothing to work with, and the resulting curve is flat at a tiny value. This is a description of your data, not a defect in your analysis.

The second is a table dominated by rows of zeros or near-zeros. If tens of thousands of genes have counts only in a handful of libraries, the bulk of the table contributes nothing but floor-level estimates. This overlaps with Cause 2, but the remedy differs. Here the fix is proper pre-filtering to remove dead rows before `DESeq()`, which both speeds the run and gives the trend fit a defensible input set.

Confirm both situations with:

```r
summary(mcols(dds)$dispGeneEst)
table(rowSums(counts(dds)) == 0)
```

A large cluster of estimates at the floor value, combined with many all-zero rows, points here. After filtering, rerun and re-plot.

### Cause 4: The wrong input matrix

DESeq2 requires unnormalized, raw integer counts. This is stated plainly in the package documentation, and violating it distorts everything downstream, including the dispersion fit. Common mistakes that can produce odd trend behavior:

- Loading TPM, FPKM, or CPM values, which are length-normalized or library-normalized and destroy the count-variance relationship the model depends on.
- Loading counts that have already been size-factor normalized or rounded from normalized values.
- Supplying a matrix of transcript-level estimated counts without proper offset handling instead of aggregated gene-level counts.

Check the matrix you actually passed in:

```r
class(counts(dds)[1, 1])
all(counts(dds) == round(counts(dds)))
range(counts(dds))
```

Counts must be integers. If they are not, fix the import step before anything else. For transcript quantifiers such as Salmon or kallisto, import estimated counts at the gene level with `tximport`, which handles the offsets correctly, then pass the resulting object to `DESeqDataSetFromTximport`. No amount of fit-type tuning will rescue an analysis built on the wrong matrix, so this check comes early in the diagnostic sequence.

### Cause 5: Very few replicates

Dispersion estimation consumes residual degrees of freedom. With two samples per group, the gene-wise estimate for each gene rests on almost no within-group information, and the estimates across genes become a cloud of noise with weak structure. A trend fit through a noisy cloud can settle on a flat line even when a real mean-dependence exists in the data.

Confirm by examining group sizes:

```r
colData(dds)
table(colData(dds)$condition)
```

The DESeq2 documentation and its companion paper recommend, as a matter of design, that biological replicates number three or more per group, with power improving well beyond that. If you are at the minimum, a flat trend warning is one of several signs that gene-wise estimates are unstable. Practical responses:

- Add replicates. This is the only change that genuinely improves dispersion estimation, and the improvement is far larger than any software option.
- Compare `fitType = "parametric"`, `"local"`, and `"mean"` runs. When estimates are noisy, the choice of shrinkage target matters more, so agreement between fit types is informative and disagreement is a caution.
- Interpret modest fold changes with extra care and lean on effect-size thresholds when reporting.

### Cause 6: Outlier genes dominating the fit

The parametric fit uses a robust fitting procedure precisely because a small number of genes with extreme dispersion estimates can drag the curve. If a handful of genes have estimates orders of magnitude above the rest, for example genes whose counts vary wildly in a single sample, the robust fit can end up flattening the curve to resist their pull, triggering the warning while the bulk of genes actually show a normal declining trend.

Confirm with the plot first. Then look at flagged outliers after testing:

```r
res <- results(dds)
summary(res)
```

The summary prints how many genes were flagged as outliers by Cook's distance. DESeq2 automatically replaces outlier counts with values shrunk toward the median when a group has at least seven replicates, controlled by the `minReplicatesForReplace` argument, so this machinery engages only in sufficiently replicated designs. In smaller designs, outliers are flagged but not repaired, and your options are to inspect the offending genes for technical artifacts, document them, and proceed, or to use the `results()` argument `cooksCutoff = FALSE` with an explicit justification, which the documentation suggests pairing with manual outlier inspection.

The connection to the flat trend is that cleaning up the outliers, either through replacement in well-replicated data or through principled exclusion of a demonstrated technical failure, often lets the parametric fit recover a normal shape on the next run.

### Cause 7: Data types where the negative binomial model is strained

The rarest group. Certain count data have variance structure dominated by a constant coefficient of variation, meaning variance grows roughly with the square of the mean across the whole range. Under the DESeq2 variance model, constant coefficient of variation corresponds to a dispersion curve that is nearly flat, so the warning is the model correctly detecting a real property of the data. Reported cases include metagenomic abundance tables, certain protein interaction or CLIP-style count data, and allele-specific counting schemes where counts are correlated across features within a sample.

In these settings the warning is expected and often appears reproducibly across datasets of the same type. The question shifts from "why is the trend flat" to "is DESeq2 the right tool and is the design right". The mean-dispersion fit will still shrink estimates sensibly, the negative binomial GLM still applies, and many analyses of such data proceed with DESeq2 after acknowledging the fit shape. If the flat trend co-occurs with other signs of model strain, such as systematically non-uniform p-value distributions under the null, consider whether the data need transformation, aggregation, or a different modeling framework. That decision belongs to the specific analysis, and the warning itself is the starting observation rather than the final diagnosis.

## How to Fix the Flat Dispersion Trend in DESeq2: Practical Solutions

The fixes fall into three tiers. Match the tier to what your diagnostics showed.

### Tier 1: Accept the fit when the plot looks right

If `plotDispEsts` shows the red curve passing through the point cloud, even flat, you need no code change. The curve is an accurate summary of your data. Record the warning and the plot in your workflow notes. This is the correct outcome for Cause 1, and it is more common than most analysts expect.

### Tier 2: Change the fit type

![Comparison table of the parametric, local and mean dispersion fit types showing their shape, when each is fragile, and when to choose it.](/article-images/illustrations/deseq2-dispersion-trend-flat-fit-type-choice.webp)

If the plot shows the curve missing the data, or if the fit input is small, rerun with an alternative fit type.

```r
ddsLocal <- DESeq(dds, fitType = "local")
plotDispEsts(ddsLocal)
```

The local fit smooths the gene-wise estimates without imposing the 1/mean plus intercept form, so it can express whatever shape the data show, including shapes the parametric form cannot. It is the standard recommendation whenever the parametric fit misbehaves, and DESeq2 itself turns to it automatically when the parametric fit fails outright.

```r
ddsMean <- DESeq(dds, fitType = "mean")
plotDispEsts(ddsMean)
```

The mean fit uses one constant dispersion, taken from the gene-wise estimates, for all genes. With very few genes or extremely noisy estimates, this is the most stable target, and the cost in model flexibility is small.

Compare the impact on results rather than comparing the curves alone:

```r
resP <- results(dds)
resL <- results(ddsLocal)
resM <- results(ddsMean)

sum(resP$padj < 0.1, na.rm = TRUE)
sum(resL$padj < 0.1, na.rm = TRUE)
sum(resM$padj < 0.1, na.rm = TRUE)
```

Here 0.1 is the DESeq2 default adjusted p-value threshold, which you should change to match your project's alpha. If the number of significant genes and the ranking of top hits are similar across fit types, the warning had little practical effect and you can report the default run with a note. If they differ substantially, the flat trend was distorting the shrinkage, and you should choose the fit whose curve best matches the gene-wise points in the plot and say so in your methods.

### Tier 3: Fix the input

If the diagnostics pointed to Cause 3 or 4, no fit type will produce a trustworthy trend until the matrix itself is right.

- Re-import raw integer gene-level counts from the aligner or quantifier output.
- For transcript-level quantification, go through `tximport` so that lengths and offsets are handled as the DESeq2 workflow expects.
- Apply the light pre-filter shown above to remove rows of near-total zeros.
- Verify size factors before dispersion estimation:

```r
dds <- estimateSizeFactors(dds)
summary(sizeFactors(dds))
```

Size factors should mostly stay within roughly a factor of a few of each other. A factor spanning orders of magnitude signals a broken library or a normalization problem that will distort every downstream estimate, dispersion trend included.

### What not to do as a fix

Do not pre-normalize the counts to "stabilize" the fit. DESeq2 expects raw counts and applies its own normalization. Do not delete genes until the warning disappears, because a filter chosen to silence a warning rather than to remove dead rows changes the analysis. Do not switch to `fitType = "local"` permanently and unthinkingly across all future projects, because the parametric fit remains the better-justified default when it works.

## A Diagnostic Sequence You Can Follow

Run these steps in order the next time the warning appears. The whole sequence takes a few minutes and resolves most cases.

1. **Capture the exact message and the step.** Note whether it appeared during the dispersion fit inside `DESeq()`. A warning that continues the run is different from an error that stops it.
2. **Plot the fit.** `plotDispEsts(dds)`. Classify the pattern as A (curve tracks dots, benign), B (dots piled at the floor), or C (curve misses the dots).
3. **Count the fit input.** Check `nrow(dds)`, `sum(mcols(dds)$allZero)`, and the distribution of `mcols(dds)$baseMean`. Few genes or a huge mass of near-zero means points to Cause 2 or 3.
4. **Inspect the fitted coefficients.** `attr(dispersionFunction(dds), "coefficients")` shows the two fitted values of the parametric curve. A slope term that is negligible relative to the intercept confirms the flat fit mechanically.
5. **Verify the input matrix.** Integer check, value range, and the import path. Rule out TPM, FPKM, or pre-normalized values.
6. **Check size factors.** `summary(sizeFactors(dds))`. Extreme spread points to library problems first, dispersion second.
7. **Check group sizes and design.** Very small groups make every gene-wise estimate noisy, which feeds Causes 2 and 5.
8. **Rerun with `fitType = "local"` and `fitType = "mean"`.** Plot each and compare significant gene counts against the default run.
9. **Check outlier flags.** `summary(results(dds))` after the default run. In well-replicated designs, outlier replacement may change the fit on rerun.
10. **Record versions.** `packageVersion("DESeq2")` and `sessionInfo()` in your notebook. Behavior around dispersion fitting has changed across releases, and the version belongs in the record.

## Common Mistakes and Limitations

**Treating the warning as a failure.** The message is a warning, not an error. The run finishes, results are produced, and the dispersion fit exists. The mistake is to abandon or restart the analysis without first looking at the plot, which is the only evidence that matters.

**Confusing this warning with the parametric fit failure.** A separate message tells you the parametric fit failed outright and DESeq2 fell back to the local fit automatically. That message describes an action already taken. The flat-trend warning describes a completed fit whose shape you should inspect. The two calls for different responses, and mixing them up leads people to rerun with `fitType = "local"` when DESeq2 has already done exactly that.

**Fixing the matrix with normalization.** Applying CPM, TPM, or variance-stabilizing transformations before `DESeqDataSetFromMatrix` invalidates the model. Transformations belong after the fit, for visualization, through `vst()` or `rlog()`, not before it.

**Over-filtering to change the fit.** Filtering should remove rows of near-total zeros, as in the recommended example of ten or more reads in at least three samples. Filtering to a small curated gene list, then wondering why the trend fit is unstable, inverts the logic.

**Reading the flat trend as proof of a problem in the biology.** A flat trend can mean the data lack a mean-dispersion relationship. That is a property of the experiment, and in controlled systems it is unremarkable. The plot, not the warning text, decides this.

**Ignoring genuine limitations.** With two replicates per group, no fit type can manufacture reliable dispersion estimates, and results from such designs should be framed as exploratory regardless of warnings. With unusual data types such as metagenomic tables, the flat trend may be a signal that the standard RNA-seq workflow fits the data imperfectly. One general limitation applies to every analysis here: conclusions from a single dataset need validation, ideally experimental, before they support strong claims.

## Prevention Practices

Most flat-trend warnings are avoidable or trivially explainable with a fixed routine.

- **Always start from raw counts.** Keep the aligner or quantifier output as the immutable input, and document the import script in the project repository.
- **Pre-filter lightly and consistently.** Remove rows of near-total zeros with the standard pattern, not with ad hoc thresholds tuned per run.
- **Check size factors before dispersion fitting.** One line with `summary(sizeFactors(dds))` catches broken libraries early.
- **Make `plotDispEsts` a mandatory step.** Save the figure for every run, in every project. The trend warning is one of several fit behaviors the plot reveals, and a saved series of these plots is a valuable QC record across projects.
- **Write down the warning and the response.** A methods note that says the warning appeared, the plot was inspected, and results were stable across fit types closes the question permanently.
- **Pin and record versions.** Record `packageVersion("DESeq2")` and R version with each analysis, since fit behavior and message handling evolve between releases.
- **Design for replication.** Three or more biological replicates per group, and more where the budget allows, keeps gene-wise estimates stable and makes every dispersion diagnostic easier to read.

## Frequently Asked Questions

### Does the "dispersion trend is flat" warning mean my DESeq2 run failed?

No. The warning is informational and the run continues to completion, producing size factors, fitted dispersions, and test results. It is a prompt to inspect the fitted curve with `plotDispEsts`, not a sign of failure.

### Can I safely ignore the warning?

Only after checking the plot. If the red fitted curve passes through the cloud of black gene-wise points, the fit describes your data accurately and you can proceed. If the curve misses the points, apply one of the fixes in this article before trusting the results.

### Should I always switch to fitType = "local" when I see this warning?

No. Switch to `fitType = "local"` when the plot shows the parametric curve missing the gene-wise points, or when few genes feed the fit. When the plot looks correct, the parametric fit remains the better-justified choice, and a comparison across fit types is the most informative response.

### What causes a flat dispersion trend in RNA-seq data?

The most common causes are a genuinely weak mean-dispersion relationship, too few genes feeding the fit after filtering, near-Poisson or zero-heavy count matrices, wrong input such as normalized or non-integer values, and very few replicates that make gene-wise estimates noisy.

### Why does my small dataset trigger this warning?

Small datasets hit the warning through two routes. Few genes leave the trend fit with too few points to estimate its shape, and few replicates leave each gene-wise estimate so noisy that the fitted curve loses its expected declining shape. Both routes are visible in `plotDispEsts` and in the group sizes in `colData`.

### Does this warning change my p-values or the list of differentially expressed genes?

Potentially, yes. The fitted trend is the shrinkage target for the final dispersions used in testing, so a distorted trend can shift dispersions and therefore p-values, mainly for highly expressed genes. Comparing significant gene counts across parametric, local, and mean fits tells you whether the warning had a practical effect in your data.

### What is the difference between "dispersion trend is flat" and the parametric fit failure message?

The flat-trend warning says the parametric fit converged but its curve is nearly horizontal, and DESeq2 continues with it. The fit failure message says the parametric fit did not produce a usable curve, and DESeq2 automatically refits with the local fit. Read the message text carefully before choosing a response.

### How do I inspect the dispersion fit in my own session?

Run `plotDispEsts(dds)` for the visual summary, and `attr(dispersionFunction(dds), "coefficients")` for the two fitted values of the parametric curve. The fitted per-gene values are also stored in the `dispFit` column of the row metadata, accessible with `mcols(dds)$dispFit`.

<script type="application/ld+json">
{
  "@context": "https://schema.org",
  "@type": "FAQPage",
  "mainEntity": [
    {
      "@type": "Question",
      "name": "Does the 'dispersion trend is flat' warning mean my DESeq2 run failed?",
      "acceptedAnswer": {
        "@type": "Answer",
        "text": "No. The warning is informational and the run continues to completion, producing size factors, fitted dispersions, and test results. It is a prompt to inspect the fitted curve with plotDispEsts, not a sign of failure."
      }
    },
    {
      "@type": "Question",
      "name": "Can I safely ignore the warning?",
      "acceptedAnswer": {
        "@type": "Answer",
        "text": "Only after checking the plot. If the red fitted curve passes through the cloud of black gene-wise points, the fit describes your data accurately and you can proceed. If the curve misses the points, apply one of the fixes before trusting the results."
      }
    },
    {
      "@type": "Question",
      "name": "Should I always switch to fitType = \"local\" when I see this warning?",
      "acceptedAnswer": {
        "@type": "Answer",
        "text": "No. Switch to the local fit when the plot shows the parametric curve missing the gene-wise points, or when few genes feed the fit. When the plot looks correct, the parametric fit remains the better-justified choice, and a comparison across fit types is the most informative response."
      }
    },
    {
      "@type": "Question",
      "name": "What causes a flat dispersion trend in RNA-seq data?",
      "acceptedAnswer": {
        "@type": "Answer",
        "text": "The most common causes are a genuinely weak mean-dispersion relationship, too few genes feeding the fit after filtering, near-Poisson or zero-heavy count matrices, wrong input such as normalized or non-integer values, and very few replicates that make gene-wise estimates noisy."
      }
    },
    {
      "@type": "Question",
      "name": "Why does my small dataset trigger this warning?",
      "acceptedAnswer": {
        "@type": "Answer",
        "text": "Small datasets hit the warning through two routes. Few genes leave the trend fit with too few points to estimate its shape, and few replicates leave each gene-wise estimate so noisy that the fitted curve loses its expected declining shape."
      }
    },
    {
      "@type": "Question",
      "name": "Does this warning change my p-values or the list of differentially expressed genes?",
      "acceptedAnswer": {
        "@type": "Answer",
        "text": "Potentially, yes. The fitted trend is the shrinkage target for the final dispersions used in testing, so a distorted trend can shift p-values, mainly for highly expressed genes. Comparing significant gene counts across parametric, local, and mean fits tells you whether the warning had a practical effect."
      }
    },
    {
      "@type": "Question",
      "name": "What is the difference between 'dispersion trend is flat' and the parametric fit failure message?",
      "acceptedAnswer": {
        "@type": "Answer",
        "text": "The flat-trend warning says the parametric fit converged but its curve is nearly horizontal, and DESeq2 continues with it. The fit failure message says the parametric fit did not produce a usable curve, and DESeq2 automatically refits with the local fit."
      }
    },
    {
      "@type": "Question",
      "name": "How do I inspect the dispersion fit in my own session?",
      "acceptedAnswer": {
        "@type": "Answer",
        "text": "Run plotDispEsts(dds) for the visual summary, and attr(dispersionFunction(dds), \"coefficients\") for the two fitted values of the parametric curve. Fitted per-gene values are stored in the dispFit column of the row metadata."
      }
    }
  ]
}
</script>