neondatabase/neon · critical
Non-overlappinng bounds: self.max = {} was less than other.m
Error message
Non-overlappinng bounds: self.max = {} was less than other.min = {} What it means
Mirror case of `ProtocolRange::highest_shared_version`: bails when `self.max < other.min`, i.e. this side's highest known version is below the peer's lowest acceptable version, so the ranges cannot overlap. Note the message contains a typo ('Non-overlappinng') — log greps must match the misspelling.
Source
Thrown at libs/vm_monitor/src/protocol.rs:225
f.write_fmt(format_args!("{} to {}", self.min, self.max))
}
}
}
impl ProtocolRange {
/// Find the highest shared version between two `ProtocolRange`'s
pub fn highest_shared_version(&self, other: &Self) -> anyhow::Result<ProtocolVersion> {
// We first have to make sure the ranges are overlapping. Once we know
// this, we can merge the ranges by taking the max of the mins and the
// mins of the maxes.
if self.min > other.max {
anyhow::bail!(
"Non-overlapping bounds: other.max = {} was less than self.min = {}",
other.max,
self.min,
)
} else if self.max < other.min {
anyhow::bail!(
"Non-overlappinng bounds: self.max = {} was less than other.min = {}",
self.max,
other.min
)
} else {
Ok(cmp::min(self.max, other.max))
}
}
}
/// We send this to the monitor after negotiating which protocol to use
#[derive(Serialize, Debug)]
#[serde(rename_all = "camelCase")]
pub enum ProtocolResponse {
Error(String),
Version(ProtocolVersion),
}
View on GitHub (pinned to 8f60b04da4)
Solutions
- Upgrade this component (the one whose max is too low) to a build supporting the peer's minimum version
- Or have the peer lower its minimum protocol version until this side catches up
- Grep logs for 'Non-overlappinng' (sic) and 'Non-overlapping' — both spellings occur on the two sides of the check
- Print both ProtocolRange values at handshake to confirm which pair is disjoint
Example fix
// before: peer requires >= v2.0, we only know up to v1.0
let my_range = ProtocolRange { min: PROTOCOL_MIN_VERSION, max: PROTOCOL_MAX_VERSION }; // v1.0..v1.0
my_range.highest_shared_version(&peer_min_v2_range)?; // bails: self.max < other.min
// after: upgrade so this side's max covers the peer's min
let my_range = ProtocolRange { min: PROTOCOL_MIN_VERSION, max: ProtocolVersion::V2_0 };
my_range.highest_shared_version(&peer_min_v2_range)?; // Ok(v2.0) Defensive patterns
Strategy: validation
Validate before calling
fn ranges_overlap(a: &ProtocolRange, b: &ProtocolRange) -> bool {
a.min <= b.max && a.max >= b.min
}
if !ranges_overlap(&my_range, &peer_range) {
anyhow::bail!("peer requires {}, we only support up to {}", peer_range.min, my_range.max);
} Type guard
fn ranges_overlap(a: &ProtocolRange, b: &ProtocolRange) -> bool {
a.min <= b.max && a.max >= b.min
} Prevention
- Roll out protocol bumps so each side's max always covers the other's current min
- Grep for both 'Non-overlapping' and the misspelled 'Non-overlappinng' when searching logs
- Include a version-skew integration test between oldest and newest supported builds
When it happens
Trigger: Calling `highest_shared_version(&other)` where the peer demands a minimum version above this side's maximum — e.g. an upgraded agent with PROTOCOL_MIN_VERSION v2.0 talking to a monitor that only knows up to v1.0.
Common situations: Rolling upgrades where the new peer's minimum jumped past the old side's maximum; protocol constants drift between builds; tests with disjoint hardcoded ranges.
Related errors
- Non-overlapping bounds: other.max = {} was less than self.mi
- incompatible resource_multipler and spread_factor
- file cache size query returned no rows
- max file cache size query returned no rows
- invalid monitor Config: sys_buffer_bytes cannot be 0
AI-assisted analysis of neondatabase/neon@8f60b04da4 (2026-08-16).
Data as JSON: /api/errors/16af108b6d8c757b.
Report an issue: GitHub.