{"record":{"id":"1235e03c37462c1f","repo":"risingwavelabs/risingwave","slug":"expect-state-store","errorCode":null,"errorMessage":"expect state_store","messagePattern":"expect state_store","errorType":"exception","errorClass":"BackupError","httpStatus":null,"severity":"error","filePath":"src/storage/backup/src/meta_snapshot_v2.rs","lineNumber":213,"sourceCode":"    }\n\n    fn hummock_version_ref(&self) -> &HummockVersion {\n        &self.hummock_version\n    }\n\n    fn hummock_version(self) -> HummockVersion {\n        self.hummock_version\n    }\n\n    fn storage_url(&self) -> BackupResult<String> {\n        let storage_url_from_snapshot =\n            Itertools::exactly_one(self.system_parameters.iter().filter_map(|m| {\n                if m.name == \"state_store\" {\n                    return Some(m.value.clone());\n                }\n                None\n            }))\n            .map_err(|_| BackupError::Other(anyhow!(\"expect state_store\")))?;\n        storage_url_from_snapshot\n            .strip_prefix(\"hummock+\")\n            .map(|s| s.to_owned())\n            .ok_or_else(|| {\n                BackupError::Other(anyhow!(\n                    \"invalid state_store from metadata snapshot: {}\",\n                    storage_url_from_snapshot\n                ))\n            })\n    }\n\n    fn storage_directory(&self) -> BackupResult<String> {\n        Itertools::exactly_one(self.system_parameters.iter().filter_map(|m| {\n            if m.name == \"data_directory\" {\n                return Some(m.value.clone());\n            }\n            None\n        }))","sourceCodeStart":195,"sourceCodeEnd":231,"githubUrl":"https://github.com/risingwavelabs/risingwave/blob/6469eb736d691e8e9b8a419a57edd6429ca77417/src/storage/backup/src/meta_snapshot_v2.rs#L195-L231","documentation":"MetaSnapshotV2::storage_url extracts the state_store system parameter from the snapshot's system_parameters list and requires exactly one entry named \"state_store\"; otherwise it throws \"expect state_store\". The snapshot must record the Hummock state store URL for restore to know where storage lives.","triggerScenarios":"Calling storage_url() on a decoded V2 meta snapshot whose system_parameters list has zero or multiple entries named \"state_store\" — e.g. parameters were never persisted, an older snapshot omitted them, or a corrupted/duplicated parameters section.","commonSituations":"Restoring a backup created without system parameters recorded; a backup made from a cluster configured unusually (parameters split across nodes) producing duplicates; hand-edited or partially decoded snapshots used in tests; old backup format missing the field.","solutions":["Verify the backup's system_parameters section contains exactly one state_store entry (inspect with risectl or decode the snapshot).","Recreate the backup from a healthy cluster so system parameters (state_store, data_directory) are captured.","If restoring from a legacy snapshot, use the matching older restore path or supply the state store URL manually.","Check for duplicated parameter rows in the source cluster's system_parameter table before snapshotting."],"exampleFix":"// before: assuming the field exists\nlet url = snapshot.storage_url()?;\n// after: check presence and fall back to operator-supplied config\nlet url = match snapshot.storage_url() {\n    Ok(u) => u,\n    Err(_) => read_state_store_from_restore_config()?,\n};","handlingStrategy":"fallback","validationCode":"let n = snapshot.system_parameters.iter().filter(|m| m.name == \"state_store\").count();\nif n != 1 { return Err(anyhow!(\"snapshot has {} state_store entries, expected 1\", n)); }","typeGuard":"fn has_single_param<'a>(ps: &'a [SystemParam], name: &str) -> Option<&'a str> {\n    let mut it = ps.iter().filter(|p| p.name == name).map(|p| p.value.as_str());\n    match (it.next(), it.next()) { (Some(v), None) => Some(v), _ => None }\n}","tryCatchPattern":"match snapshot.storage_url() {\n    Ok(url) => url,\n    Err(e) if e.to_string().contains(\"expect state_store\") => {\n        log::warn!(\"snapshot lacks state_store; using restore config\");\n        restore_config.state_store.clone()\n    }\n    Err(e) => return Err(e.into()),\n}","preventionTips":["Ensure backups are taken from clusters where system parameters are fully persisted.","Validate that exactly one state_store parameter row exists before snapshotting.","Inspect snapshot parameters with risectl after each backup as a health check."],"tags":["rust","backup","restore","system-parameters","missing-field"],"backgroundTag":"missing-required-config-field","analyzedSha":"6469eb736d691e8e9b8a419a57edd6429ca77417","analyzedAt":"2026-09-11T21:06:21.487Z","contentChangedAt":"2026-09-11T21:06:21.487Z","schemaVersion":2},"datasetVersion":"2026-09-14T16:17:12.679Z"}