neondatabase/neon · error
incompatible resource_multipler and spread_factor
Error message
incompatible resource_multipler and spread_factor
What it means
Thrown during FileCache config validation in the Neon autoscaling vm-monitor. The cache-sizing algorithm splits total memory along two lines whose intersection must exist and stay at or above `min_remaining_after_cache`; that is only guaranteed when `resource_multiplier * (spread_factor + 1.0) < 1.0`. The `ensure!` fires when the two coupled config values violate this invariant, making the size formula unsolvable.
Source
Thrown at libs/vm_monitor/src/filecache.rs:114
// and
//
// size = `resource_multiplier` × total
//
// .. where `total` is the total resources. These are isomorphic to the typical 'y = mx + b'
// form, with y = "size" and x = "total".
//
// These lines intersect at:
//
// `min_remaining_after_cache`
// ———————————————————————————————————————————————————
// 1 - `resource_multiplier` × (`spread_factor` + 1)
//
// We want to ensure that this value (a) exists, and (b) is >= `min_remaining_after_cache`. This is
// guaranteed when '`resource_multiplier` × (`spread_factor` + 1)' is less than 1.
// (We also need it to be >= 0, but that's already guaranteed.)
let intersect_factor = self.resource_multiplier * (self.spread_factor + 1.0);
anyhow::ensure!(
intersect_factor < 1.0,
"incompatible resource_multipler and spread_factor"
);
Ok(())
}
/// Calculate the desired size of the cache, given the total memory
pub fn calculate_cache_size(&self, total: u64) -> u64 {
// *Note*: all units are in bytes, until the very last line.
let available = total.saturating_sub(self.min_remaining_after_cache.get());
if available == 0 {
return 0;
}
// Conversions to ensure we don't overflow from floating-point ops
let size_from_spread =
i64::max(0, (available as f64 / (1.0 + self.spread_factor)) as i64) as u64;
View on GitHub (pinned to 8f60b04da4)
Solutions
- Lower `resource_multiplier` so that `resource_multiplier * (spread_factor + 1.0) < 1.0` (e.g. with spread_factor 0.5, keep the multiplier below ~0.667)
- Or lower `spread_factor`; at spread_factor 0.0 the multiplier only needs to be < 1.0
- Revert to the shipped/default vm-monitor file cache configuration for your deployment
- Add a config sanity test in CI that runs the same intersect-factor check
Example fix
// before — fails validation: 0.9 * (0.5 + 1.0) = 1.35 >= 1.0 config.resource_multiplier = 0.9; config.spread_factor = 0.5; // after — passes: 0.5 * (0.5 + 1.0) = 0.75 < 1.0 config.resource_multiplier = 0.5; config.spread_factor = 0.5;
Defensive patterns
Strategy: validation
Validate before calling
if config.resource_multiplier * (config.spread_factor + 1.0) >= 1.0 {
return Err(anyhow::anyhow!(
"refusing to start: resource_multiplier * (spread_factor + 1) must be < 1.0"
));
} Prevention
- Treat resource_multiplier and spread_factor as one coupled pair and validate their product wherever config is loaded
- Add a CI test over deployed config files running the same intersect-factor check
- Keep a table of known-good (multiplier, spread) pairs next to deployment configs
When it happens
Trigger: Validating a `FileCache` config where the pair is too large together, e.g. `resource_multiplier = 0.9` with `spread_factor = 0.5` gives 0.9 × (0.5 + 1.0) = 1.35 ≥ 1.0 and bails. Any pair where the product with (spread_factor + 1) reaches or exceeds 1.0 triggers it.
Common situations: Hand-tuning vm-monitor memory settings; raising `spread_factor` to smooth cache growth without lowering `resource_multiplier`; copying configs between environments with different memory budgets.
Related errors
- resource_multiplier must be between 0.0 and 1.0 exclusive, g
- spread_factor must be >= 0, got {}
- invalid monitor Config: sys_buffer_bytes cannot be 0
- file cache size query returned no rows
- max file cache size query returned no rows
AI-assisted analysis of neondatabase/neon@8f60b04da4 (2026-08-16).
Data as JSON: /api/errors/ec82ebd65c1b9a73.
Report an issue: GitHub.