neondatabase/neon · error

invalid specifier '{first}'

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.

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)

Solutions

  1. Use 'disabled' for no compression, plain 'zstd' for the default level
  2. Specify a level in parentheses: 'zstd(9)'; negative levels are allowed since the field is i8
  3. Remove separators other than parentheses; 'zstd-9' and 'zstd:9' are invalid
  4. 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

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.

Related errors


AI-assisted analysis of neondatabase/neon@8f60b04da4 (2026-08-16). Data as JSON: /api/errors/cb19f1bac3a1e9ac. Report an issue: GitHub.