diff --git a/crates/quake/src/manifest.rs b/crates/quake/src/manifest.rs index b23a545d..6d4bbb49 100644 --- a/crates/quake/src/manifest.rs +++ b/crates/quake/src/manifest.rs @@ -886,6 +886,12 @@ impl Manifest { bail!("At least one node must be defined"); } + for node_name in self.nodes.keys() { + if self.subnets.subnets_of(node_name).is_empty() { + bail!("Node '{node_name}' must belong to at least one subnet"); + } + } + if let Some(gb) = self.node_disk_gb { if gb < MIN_DISK_GB { bail!("node_disk_gb must be at least {MIN_DISK_GB} (got {gb})"); diff --git a/crates/quake/src/manifest/raw.rs b/crates/quake/src/manifest/raw.rs index b87cedca..6474674d 100644 --- a/crates/quake/src/manifest/raw.rs +++ b/crates/quake/src/manifest/raw.rs @@ -848,6 +848,23 @@ mod tests { use super::*; + #[test] + fn test_empty_node_subnets_are_rejected() { + let toml = r#" + [nodes.validator1] + subnets = ["trusted"] + + [nodes.full1] + subnets = [] + "#; + + let err = Manifest::from_string(toml).unwrap_err().to_string(); + assert!( + err.contains("Node 'full1' must belong to at least one subnet"), + "unexpected error: {err}" + ); + } + /// el.config.trusted_peers round-trips through RawManifest → Manifest → RawManifest → Manifest. #[test] fn test_el_trusted_peers_roundtrip() {