neondatabase/neon · error
invalid specifier
Error message
invalid specifier '{first}' What it means
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.
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
Example fix
# before (pageserver.toml) image_compression = 'zstd-9' # -> invalid specifier 'zstd-9' # after image_compression = 'zstd(9)'
Defensive patterns
Strategy: type-guard
Validate before calling
fn validate_image_compression(s: &str) -> Result<(), String> {
ImageCompressionAlgorithm::from_str(s)
.map(|_| ())
.map_err(|e| format!("bad image_compression {s:?}: {e:#}; use 'disabled', 'zstd', or 'zstd(<i8>)'"))
} Type guard
/// Mirrors ImageCompressionAlgorithm::from_str without panicking.
fn is_valid_image_compression(s: &str) -> bool {
let mut parts = s.split(['(', ')']);
match parts.next() {
Some("disabled") => true,
Some("zstd") => match parts.next() {
None => true, // plain "zstd"
Some(lvl) => lvl.parse::<i8>().is_ok(),
},
_ => false,
}
} Try / catch
match ImageCompressionAlgorithm::from_str(&value) {
Ok(a) => a,
Err(e) if e.to_string().contains("invalid specifier") => {
return Err(anyhow::anyhow!(
"{e}; accepted specifiers: 'disabled', 'zstd', 'zstd(<i8 level>)' (e.g. 'zstd(9)')"
));
}
Err(e) => return Err(e), // level parse failure, e.g. zstd(300) overflowing i8
} Prevention
- 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
When it happens
Trigger: 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.)
Common situations: 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.
Understand the failure class
Background: "Invalid value" and "allowed values are" config errors: what your library rejected and how to fix it — this error's family across 41 libraries.
Related errors
- must have at least one pageserver
- no shards provided
- pageserver connection information should be provided
- shard missing from pageserver_connection_info shard map
- stripe size can't be given for unsharded tenants
AI-assisted analysis of neondatabase/neon@8f60b04da4 (2026-08-16).
Data as JSON: /api/errors/cb19f1bac3a1e9ac.
Report an issue: GitHub.
Appendix: source
Thrown at libs/pageserver_api/src/models.rs:1204
type Err = anyhow::Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let mut components = s.split(['(', ')']);
let first = components
.next()
.ok_or_else(|| anyhow::anyhow!("empty string"))?;
match first {
"disabled" => Ok(ImageCompressionAlgorithm::Disabled),
"zstd" => {
let level = if let Some(v) = components.next() {
let v: i8 = v.parse()?;
Some(v)
} else {
None
};
Ok(ImageCompressionAlgorithm::Zstd { level })
}
_ => anyhow::bail!("invalid specifier '{first}'"),
}
}
}
impl Display for ImageCompressionAlgorithm {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
ImageCompressionAlgorithm::Disabled => write!(f, "disabled"),
ImageCompressionAlgorithm::Zstd { level } => {
if let Some(level) = level {
write!(f, "zstd({level})")
} else {
write!(f, "zstd")
}
}
}
}
}View on GitHub (pinned to 8f60b04da4)