{"record":{"id":"cb19f1bac3a1e9ac","repo":"neondatabase/neon","slug":"invalid-specifier-first","errorCode":null,"errorMessage":"invalid specifier '{first}'","messagePattern":"invalid specifier '(.+?)'","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"libs/pageserver_api/src/models.rs","lineNumber":1204,"sourceCode":"    type Err = anyhow::Error;\n    fn from_str(s: &str) -> Result<Self, Self::Err> {\n        let mut components = s.split(['(', ')']);\n        let first = components\n            .next()\n            .ok_or_else(|| anyhow::anyhow!(\"empty string\"))?;\n        match first {\n            \"disabled\" => Ok(ImageCompressionAlgorithm::Disabled),\n            \"zstd\" => {\n                let level = if let Some(v) = components.next() {\n                    let v: i8 = v.parse()?;\n                    Some(v)\n                } else {\n                    None\n                };\n\n                Ok(ImageCompressionAlgorithm::Zstd { level })\n            }\n            _ => anyhow::bail!(\"invalid specifier '{first}'\"),\n        }\n    }\n}\n\nimpl Display for ImageCompressionAlgorithm {\n    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {\n        match self {\n            ImageCompressionAlgorithm::Disabled => write!(f, \"disabled\"),\n            ImageCompressionAlgorithm::Zstd { level } => {\n                if let Some(level) = level {\n                    write!(f, \"zstd({level})\")\n                } else {\n                    write!(f, \"zstd\")\n                }\n            }\n        }\n    }\n}","sourceCodeStart":1186,"sourceCodeEnd":1222,"githubUrl":"https://github.com/neondatabase/neon/blob/8f60b04da47ffefe0e52bda2440134b42874eb75/libs/pageserver_api/src/models.rs#L1186-L1222","documentation":"ImageCompressionAlgorithm::from_str parses the pageserver's image compression setting by splitting the string on '(' and ')'. The first component must be 'disabled' or 'zstd'; 'zstd' may carry one parenthesized level component parsed as i8 ('zstd(9)'). Any other first component (empty string aside, which has its own error) raises this 'invalid specifier' bail. Used for the image_compression pageserver configuration.","triggerScenarios":"Setting image_compression in pageserver.toml or the HTTP config API to an unsupported algorithm or syntax: 'zstd-9', 'lz4', 'zstd:9', 'Zstd(9)', or 'zstd[' -- the first split component is not 'disabled' or 'zstd'. (A bad level, e.g. 'zstd(300)' or 'zstd(abc)', instead surfaces the i8 parse error.)","commonSituations":"Porting settings written for a different compression library's syntax (hyphen or colon separators); assuming old/new format strings like 'zstd9' work; typos and casing differences; docs examples that predate the parser.","solutions":["Use 'disabled' for no compression, plain 'zstd' for the default level","Specify a level in parentheses: 'zstd(9)'; negative levels are allowed since the field is i8","Remove separators other than parentheses; 'zstd-9' and 'zstd:9' are invalid","Check the Display impl round-trip: a value printed by the API is always re-parseable"],"exampleFix":"# before (pageserver.toml)\nimage_compression = 'zstd-9'\n# -> invalid specifier 'zstd-9'\n\n# after\nimage_compression = 'zstd(9)'","handlingStrategy":"type-guard","validationCode":"fn validate_image_compression(s: &str) -> Result<(), String> {\n    ImageCompressionAlgorithm::from_str(s)\n        .map(|_| ())\n        .map_err(|e| format!(\"bad image_compression {s:?}: {e:#}; use 'disabled', 'zstd', or 'zstd(<i8>)'\"))\n}","typeGuard":"/// Mirrors ImageCompressionAlgorithm::from_str without panicking.\nfn is_valid_image_compression(s: &str) -> bool {\n    let mut parts = s.split(['(', ')']);\n    match parts.next() {\n        Some(\"disabled\") => true,\n        Some(\"zstd\") => match parts.next() {\n            None => true,           // plain \"zstd\"\n            Some(lvl) => lvl.parse::<i8>().is_ok(),\n        },\n        _ => false,\n    }\n}","tryCatchPattern":"match ImageCompressionAlgorithm::from_str(&value) {\n    Ok(a) => a,\n    Err(e) if e.to_string().contains(\"invalid specifier\") => {\n        return Err(anyhow::anyhow!(\n            \"{e}; accepted specifiers: 'disabled', 'zstd', 'zstd(<i8 level>)' (e.g. 'zstd(9)')\"\n        ));\n    }\n    Err(e) => return Err(e), // level parse failure, e.g. zstd(300) overflowing i8\n}","preventionTips":["Copy-paste values from the API's own Display output (always round-trippable) instead of inventing syntax","Validate configuration files in a pre-deploy step by calling FromStr on every enum-like setting","Reject hyphen/colon level syntax ('zstd-9', 'zstd:9') in docs examples -- only parentheses are valid","Remember levels are i8: negative allowed, values beyond -128..127 fail parsing"],"tags":["rust","pageserver","configuration","compression","validation"],"backgroundTag":"invalid-config-value","analyzedSha":"8f60b04da47ffefe0e52bda2440134b42874eb75","analyzedAt":"2026-08-16T23:39:28.135Z","schemaVersion":2},"datasetVersion":"2026-08-17T04:17:16.089Z"}