{"record":{"id":"c767c328e191b533","repo":"neondatabase/neon","slug":"failed-to-parse-layer-name-e","errorCode":null,"errorMessage":"failed to parse layer name: {e}","messagePattern":"failed to parse layer name: (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pageserver/ctl/src/layer_map_analyzer.rs","lineNumber":80,"sourceCode":"        let start = match self\n            .holes\n            .binary_search_by_key(&key_range.start, |hole| hole.0.start)\n        {\n            Ok(index) => index,\n            Err(index) => {\n                if index == 0 {\n                    return false;\n                }\n                index - 1\n            }\n        };\n        self.holes[start].0.end >= key_range.end\n    }\n}\n\npub(crate) fn parse_filename(name: &str) -> anyhow::Result<LayerFile> {\n    let layer_name =\n        LayerName::from_str(name).map_err(|e| anyhow!(\"failed to parse layer name: {e}\"))?;\n\n    let holes = Vec::new();\n    Ok(LayerFile {\n        key_range: layer_name.key_range().clone(),\n        lsn_range: layer_name.lsn_as_range(),\n        is_delta: layer_name.is_delta(),\n        holes,\n    })\n}\n\n// Finds the max_holes largest holes, ignoring any that are smaller than MIN_HOLE_LENGTH\"\nasync fn get_holes(path: &Utf8Path, max_holes: usize, ctx: &RequestContext) -> Result<Vec<Hole>> {\n    let file = VirtualFile::open(path, ctx).await?;\n    let file_id = page_cache::next_file_id();\n    let block_reader = FileBlockReader::new(&file, file_id);\n    let summary_blk = block_reader.read_blk(0, ctx).await?;\n    let actual_summary = Summary::des_prefix(summary_blk.as_ref())?;\n    let tree_reader = DiskBtreeReader::<_, DELTA_KEY_SIZE>::new(","sourceCodeStart":62,"sourceCodeEnd":98,"githubUrl":"https://github.com/neondatabase/neon/blob/8f60b04da47ffefe0e52bda2440134b42874eb75/pageserver/ctl/src/layer_map_analyzer.rs#L62-L98","documentation":"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.","triggerScenarios":"Pointing the analyzer at a directory that contains non-layer files (index_part.json, README, temp files) or supplying mistyped and partial layer filenames.","commonSituations":"Analyzing a whole timeline directory instead of a pure layer directory; stray files uploaded by other tooling; layer names from an incompatible naming version.","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."],"exampleFix":"# before: directory contains index_part.json alongside layers\nls timeline-dir | pageserver_ctl layer-map-analyze ...\n\n# after: filter to layer files only\nls timeline-dir | grep -E '^[0-9A-F]{40}-[0-9A-F]{25}$' | pageserver_ctl layer-map-analyze ...","handlingStrategy":"validation","validationCode":"#!/usr/bin/env bash\n# only feed generated layer filenames to the analyzer\nls \"$timeline_dir\" | grep -E '^[0-9A-F]{40}-[0-9A-F]{25}$' | pageserver_ctl layer-map-analyze ...","typeGuard":"fn is_layer_filename(name: &str) -> bool {\n    LayerName::from_str(name).is_ok()\n}","tryCatchPattern":"match parse_filename(name) {\n    Ok(layer) => layers.push(layer),\n    Err(e) if e.to_string().starts_with(\"failed to parse layer name\") => {\n        // skip non-layer files (index_part.json, READMEs) instead of aborting the analysis\n        eprintln!(\"skipping non-layer file {name}: {e}\");\n    }\n    Err(e) => return Err(e),\n}","preventionTips":["Filter directory listings to the layer filename pattern before analysis.","Prefer the pageserver's own layer listing output as a well-formed input source."],"tags":["rust","neon","ctl","layer-map","filename-parsing"],"backgroundTag":"invalid-filename-format","analyzedSha":"8f60b04da47ffefe0e52bda2440134b42874eb75","analyzedAt":"2026-08-16T23:39:28.135Z","schemaVersion":2},"datasetVersion":"2026-08-17T04:17:16.089Z"}