neondatabase/neon · error
failed to parse layer name: {e}
Error message
failed to parse layer name: {e} What it means
layer_map_analyzer reads a listing of files and parses every name with LayerName::from_str, which accepts only generated image and delta layer names (the key-range and LSN-boundary format, for example 000000067F00010001000000000000000001-0000000000000000000001). Any other string fails, and the underlying parse error is attached to this message.
Source
Thrown at pageserver/ctl/src/layer_map_analyzer.rs:80
let start = match self
.holes
.binary_search_by_key(&key_range.start, |hole| hole.0.start)
{
Ok(index) => index,
Err(index) => {
if index == 0 {
return false;
}
index - 1
}
};
self.holes[start].0.end >= key_range.end
}
}
pub(crate) fn parse_filename(name: &str) -> anyhow::Result<LayerFile> {
let layer_name =
LayerName::from_str(name).map_err(|e| anyhow!("failed to parse layer name: {e}"))?;
let holes = Vec::new();
Ok(LayerFile {
key_range: layer_name.key_range().clone(),
lsn_range: layer_name.lsn_as_range(),
is_delta: layer_name.is_delta(),
holes,
})
}
// Finds the max_holes largest holes, ignoring any that are smaller than MIN_HOLE_LENGTH"
async fn get_holes(path: &Utf8Path, max_holes: usize, ctx: &RequestContext) -> Result<Vec<Hole>> {
let file = VirtualFile::open(path, ctx).await?;
let file_id = page_cache::next_file_id();
let block_reader = FileBlockReader::new(&file, file_id);
let summary_blk = block_reader.read_blk(0, ctx).await?;
let actual_summary = Summary::des_prefix(summary_blk.as_ref())?;
let tree_reader = DiskBtreeReader::<_, DELTA_KEY_SIZE>::new(View on GitHub (pinned to 8f60b04da4)
Solutions
- Feed only layer filenames; filter the directory listing before analysis.
- Read the attached cause {e} to see which component failed (key, LSN, or layer suffix).
- Use the pageserver's own layer listing output as the input source so names are guaranteed well-formed.
Example fix
# before: directory contains index_part.json alongside layers
ls timeline-dir | pageserver_ctl layer-map-analyze ...
# after: filter to layer files only
ls timeline-dir | grep -E '^[0-9A-F]{40}-[0-9A-F]{25}$' | pageserver_ctl layer-map-analyze ... Defensive patterns
Strategy: validation
Validate before calling
#!/usr/bin/env bash
# only feed generated layer filenames to the analyzer
ls "$timeline_dir" | grep -E '^[0-9A-F]{40}-[0-9A-F]{25}$' | pageserver_ctl layer-map-analyze ... Type guard
fn is_layer_filename(name: &str) -> bool {
LayerName::from_str(name).is_ok()
} Try / catch
match parse_filename(name) {
Ok(layer) => layers.push(layer),
Err(e) if e.to_string().starts_with("failed to parse layer name") => {
// skip non-layer files (index_part.json, READMEs) instead of aborting the analysis
eprintln!("skipping non-layer file {name}: {e}");
}
Err(e) => return Err(e),
} Prevention
- Filter directory listings to the layer filename pattern before analysis.
- Prefer the pageserver's own layer listing output as a well-formed input source.
When it happens
Trigger: Pointing the analyzer at a directory that contains non-layer files (index_part.json, README, temp files) or supplying mistyped and partial layer filenames.
Common situations: Analyzing a whole timeline directory instead of a pure layer directory; stray files uploaded by other tooling; layer names from an incompatible naming version.
Understand the failure class
- Parsing and encoding errors: unexpected token, malformed input — why parsers reject input and how to find the real culprit.
Related errors
- unexpected in-memory layer
- 'REMOTE_STORAGE_CONFIG' environment variable must be set to
- unsupported linekind: {s}
- need 1..N positional arguments describing the key, try hex o
- failed to parse {one:?} as hex or span attributes: - {e1:#}
AI-assisted analysis of neondatabase/neon@8f60b04da4 (2026-08-16).
Data as JSON: /api/errors/c767c328e191b533.
Report an issue: GitHub.