{"record":{"id":"109bf5e700873036","repo":"neondatabase/neon","slug":"spread-factor-must-be-0-got","errorCode":null,"errorMessage":"spread_factor must be >= 0, got {}","messagePattern":"spread_factor must be >= 0, got (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"libs/vm_monitor/src/filecache.rs","lineNumber":81,"sourceCode":"            // 256 MiB - lower than when in memory because overcommitting is safe; if we don't have\n            // memory, the kernel will just evict from its page cache, rather than e.g. killing\n            // everything.\n            min_remaining_after_cache: NonZeroU64::new(256 * MiB).unwrap(),\n            spread_factor: 0.1,\n        }\n    }\n}\n\nimpl FileCacheConfig {\n    /// Make sure fields of the config are consistent.\n    pub fn validate(&self) -> anyhow::Result<()> {\n        // Single field validity\n        anyhow::ensure!(\n            0.0 < self.resource_multiplier && self.resource_multiplier < 1.0,\n            \"resource_multiplier must be between 0.0 and 1.0 exclusive, got {}\",\n            self.resource_multiplier\n        );\n        anyhow::ensure!(\n            self.spread_factor >= 0.0,\n            \"spread_factor must be >= 0, got {}\",\n            self.spread_factor\n        );\n\n        // Check that `resource_multiplier` and `spread_factor` are valid w.r.t. each other.\n        //\n        // As shown in `calculate_cache_size`, we have two lines resulting from `resource_multiplier` and\n        // `spread_factor`, respectively. They are:\n        //\n        //                 `total`           `min_remaining_after_cache`\n        //   size = ————————————————————— - —————————————————————————————\n        //           `spread_factor` + 1         `spread_factor` + 1\n        //\n        // and\n        //\n        //   size = `resource_multiplier` × total\n        //","sourceCodeStart":63,"sourceCodeEnd":99,"githubUrl":"https://github.com/neondatabase/neon/blob/8f60b04da47ffefe0e52bda2440134b42874eb75/libs/vm_monitor/src/filecache.rs#L63-L99","documentation":"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.","triggerScenarios":"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.","commonSituations":"Hand-tuned cache configs; values derived from telemetry deltas that go negative under noise.","solutions":["Use 0.0 or a small positive value like the default 0.1","Verify the combined constraint resource_multiplier * (spread_factor + 1.0) < 1.0 also holds","Validate config once at startup and fail fast with the message"],"exampleFix":"// before\nlet config = FileCacheConfig { spread_factor: -0.1, ..Default::default() };\nconfig.validate()?; // Err: spread_factor must be >= 0\n// after\nlet config = FileCacheConfig { spread_factor: 0.1, ..Default::default() };\nconfig.validate()?; // Ok","handlingStrategy":"validation","validationCode":"fn valid_spread_factor(v: f64) -> bool {\n    v >= 0.0\n}\n\n// also honor the cross-field rule: resource_multiplier * (spread_factor + 1.0) < 1.0\nanyhow::ensure!(valid_spread_factor(spread), \"spread_factor must be >= 0.0, got {spread}\");","typeGuard":null,"tryCatchPattern":"if let Err(e) = config.validate() {\n    if e.to_string().contains(\"spread_factor\") {\n        // config error: log the value and exit(1) with a clear message\n    }\n    return Err(e);\n}","preventionTips":["Clamp externally sourced spread_factor values to >= 0.0 before use","Validate the combined constraint resource_multiplier * (spread_factor + 1.0) < 1.0 in config tests","Prefer FileCacheConfig::default() unless tuning is actually needed"],"tags":["rust","config","validation","vm-monitor","file-cache"],"backgroundTag":"config-value-out-of-range","analyzedSha":"8f60b04da47ffefe0e52bda2440134b42874eb75","analyzedAt":"2026-08-16T23:39:28.135Z","schemaVersion":2},"datasetVersion":"2026-08-17T04:17:16.089Z"}