{"record":{"id":"f3b722f314599228","repo":"neondatabase/neon","slug":"non-overlapping-bounds-other-max-was-less-th","errorCode":null,"errorMessage":"Non-overlapping bounds: other.max = {} was less than self.min = {}","messagePattern":"Non-overlapping bounds: other\\.max = (.+?) was less than self\\.min = (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"libs/vm_monitor/src/protocol.rs","lineNumber":219,"sourceCode":"\nimpl fmt::Display for ProtocolRange {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        if self.min == self.max {\n            f.write_fmt(format_args!(\"{}\", self.max))\n        } else {\n            f.write_fmt(format_args!(\"{} to {}\", self.min, self.max))\n        }\n    }\n}\n\nimpl ProtocolRange {\n    /// Find the highest shared version between two `ProtocolRange`'s\n    pub fn highest_shared_version(&self, other: &Self) -> anyhow::Result<ProtocolVersion> {\n        // We first have to make sure the ranges are overlapping. Once we know\n        // this, we can merge the ranges by taking the max of the mins and the\n        // mins of the maxes.\n        if self.min > other.max {\n            anyhow::bail!(\n                \"Non-overlapping bounds: other.max = {} was less than self.min = {}\",\n                other.max,\n                self.min,\n            )\n        } else if self.max < other.min {\n            anyhow::bail!(\n                \"Non-overlappinng bounds: self.max = {} was less than other.min = {}\",\n                self.max,\n                other.min\n            )\n        } else {\n            Ok(cmp::min(self.max, other.max))\n        }\n    }\n}\n\n/// We send this to the monitor after negotiating which protocol to use\n#[derive(Serialize, Debug)]","sourceCodeStart":201,"sourceCodeEnd":237,"githubUrl":"https://github.com/neondatabase/neon/blob/8f60b04da47ffefe0e52bda2440134b42874eb75/libs/vm_monitor/src/protocol.rs#L201-L237","documentation":"`ProtocolRange::highest_shared_version` computes the highest protocol version shared by two inclusive [min, max] ranges during the vm-monitor agent↔monitor handshake. This variant bails when `self.min > other.max`: the lowest version one side will speak is above the highest version the peer knows, so no common version exists to negotiate.","triggerScenarios":"Calling `highest_shared_version(&other)` where the receiver's `min` exceeds the peer's `max` — e.g. a monitor compiled with PROTOCOL_MIN_VERSION above v1.0 negotiating with an older agent whose PROTOCOL_MAX_VERSION is still v1.0.","commonSituations":"Version skew after upgrading the autoscaler agent but not the vm-monitor (or vice versa); a protocol bump shipped with a raised minimum before all peers updated; hand-built ProtocolRange values in tests that don't overlap.","solutions":["Upgrade the older component so both sides' [min, max] ranges overlap","Or lower the upgraded side's minimum protocol version to still include the peer's maximum","Log the serialized ProtocolRange from both ends of the handshake to identify which side is skewed","If constructing ranges yourself, verify overlap before calling highest_shared_version"],"exampleFix":"// before: peer only supports up to v1.0, but our minimum is above that\nlet my_range = ProtocolRange { min: ProtocolVersion::V1_0.next(), max: /* ... */ };\nmy_range.highest_shared_version(&peer_range)?; // Non-overlapping bounds\n\n// after: keep the minimum at the supported baseline until peers are upgraded\nlet my_range = ProtocolRange { min: PROTOCOL_MIN_VERSION, max: PROTOCOL_MAX_VERSION };\nlet version = my_range.highest_shared_version(&peer_range)?; // Ok(v1.0)","handlingStrategy":"validation","validationCode":"fn ranges_overlap(a: &ProtocolRange, b: &ProtocolRange) -> bool {\n    a.min <= b.max && a.max >= b.min\n}\n\nif !ranges_overlap(&my_range, &peer_range) {\n    anyhow::bail!(\"no shared protocol version: {} vs {}\", my_range, peer_range);\n}\nlet v = my_range.highest_shared_version(&peer_range)?;","typeGuard":"fn ranges_overlap(a: &ProtocolRange, b: &ProtocolRange) -> bool {\n    a.min <= b.max && a.max >= b.min\n}","tryCatchPattern":null,"preventionTips":["Deploy agents and monitors together so PROTOCOL_MIN_VERSION/PROTOCOL_MAX_VERSION ranges always overlap","Log both ProtocolRange values at handshake start so skew is diagnosable","Never raise the minimum version until every peer is known to support it"],"tags":["rust","neon","vm-monitor","protocol","version-negotiation"],"backgroundTag":"protocol-version-mismatch","analyzedSha":"8f60b04da47ffefe0e52bda2440134b42874eb75","analyzedAt":"2026-08-16T23:39:28.135Z","schemaVersion":2},"datasetVersion":"2026-08-17T04:17:16.089Z"}