neondatabase/neon · error

spread_factor must be >= 0, got {}

Error message

spread_factor must be >= 0, got {}

What it means

FileCacheConfig::validate also enforces spread_factor >= 0.0. spread_factor (default 0.1) reserves bytes for the rest of the system while the cache grows, per size = (total - min_remaining_after_cache)/(spread_factor + 1); a negative value is meaningless there. Note the additional cross-check resource_multiplier * (spread_factor + 1) < 1.0.

Source

Thrown at libs/vm_monitor/src/filecache.rs:81

            // 256 MiB - lower than when in memory because overcommitting is safe; if we don't have
            // memory, the kernel will just evict from its page cache, rather than e.g. killing
            // everything.
            min_remaining_after_cache: NonZeroU64::new(256 * MiB).unwrap(),
            spread_factor: 0.1,
        }
    }
}

impl FileCacheConfig {
    /// Make sure fields of the config are consistent.
    pub fn validate(&self) -> anyhow::Result<()> {
        // Single field validity
        anyhow::ensure!(
            0.0 < self.resource_multiplier && self.resource_multiplier < 1.0,
            "resource_multiplier must be between 0.0 and 1.0 exclusive, got {}",
            self.resource_multiplier
        );
        anyhow::ensure!(
            self.spread_factor >= 0.0,
            "spread_factor must be >= 0, got {}",
            self.spread_factor
        );

        // Check that `resource_multiplier` and `spread_factor` are valid w.r.t. each other.
        //
        // As shown in `calculate_cache_size`, we have two lines resulting from `resource_multiplier` and
        // `spread_factor`, respectively. They are:
        //
        //                 `total`           `min_remaining_after_cache`
        //   size = ————————————————————— - —————————————————————————————
        //           `spread_factor` + 1         `spread_factor` + 1
        //
        // and
        //
        //   size = `resource_multiplier` × total
        //

View on GitHub (pinned to 8f60b04da4)

Solutions

  1. Use 0.0 or a small positive value like the default 0.1
  2. Verify the combined constraint resource_multiplier * (spread_factor + 1.0) < 1.0 also holds
  3. Validate config once at startup and fail fast with the message

Example fix

// before
let config = FileCacheConfig { spread_factor: -0.1, ..Default::default() };
config.validate()?; // Err: spread_factor must be >= 0
// after
let config = FileCacheConfig { spread_factor: 0.1, ..Default::default() };
config.validate()?; // Ok
Defensive patterns

Strategy: validation

Validate before calling

fn valid_spread_factor(v: f64) -> bool {
    v >= 0.0
}

// also honor the cross-field rule: resource_multiplier * (spread_factor + 1.0) < 1.0
anyhow::ensure!(valid_spread_factor(spread), "spread_factor must be >= 0.0, got {spread}");

Try / catch

if let Err(e) = config.validate() {
    if e.to_string().contains("spread_factor") {
        // config error: log the value and exit(1) with a clear message
    }
    return Err(e);
}

Prevention

When it happens

Trigger: Constructing FileCacheConfig with a negative spread_factor and calling validate(), e.g. from a sign typo in tuning code or a computed value that can dip below zero.

Common situations: Hand-tuned cache configs; values derived from telemetry deltas that go negative under noise.

Related errors


AI-assisted analysis of neondatabase/neon@8f60b04da4 (2026-08-16). Data as JSON: /api/errors/109bf5e700873036. Report an issue: GitHub.