neondatabase/neon · error
not an image or delta layer: {layer_file_path}
Error message
not an image or delta layer: {layer_file_path} What it means
When rewriting a layer summary, the ctl first tries the file as an image layer and then as a delta layer; the delta path tolerates a magic mismatch and falls through. If neither path recognizes the file's magic bytes, the file is neither a recognized image nor delta layer, and the command bails. The file is corrupt, truncated, in an incompatible on-disk format, or simply not a layer file.
Source
Thrown at pageserver/ctl/src/layers.rs:243
let res = DeltaLayer::rewrite_summary(
layer_file_path,
rewrite_closure!(delta_layer::Summary),
&ctx,
)
.await;
match res {
Ok(()) => {
println!("Successfully rewrote summary of delta layer {layer_file_path}");
return Ok(());
}
Err(delta_layer::RewriteSummaryError::MagicMismatch) => (), // fallthrough
Err(delta_layer::RewriteSummaryError::Other(e)) => {
return Err(e);
}
}
anyhow::bail!("not an image or delta layer: {layer_file_path}");
}
}
}
fn print_layer_file(idx: usize, layer_file: &LayerFile) {
println!(
"[{:3}] key:{}-{}\n lsn:{}-{}\n delta:{}",
idx,
layer_file.key_range.start,
layer_file.key_range.end,
layer_file.lsn_range.start,
layer_file.lsn_range.end,
layer_file.is_delta,
);
}
View on GitHub (pinned to 8f60b04da4)
Solutions
- Verify the file: check its size and first bytes; image and delta layers start with distinct magic values, and note that main.rs already tries pg control format before this path.
- Re-download the layer from remote storage to rule out truncation or corruption.
- Confirm the ctl build matches the pageserver version that wrote the layer.
Defensive patterns
Strategy: fallback
Validate before calling
// check the magic bytes before handing the file to layer tooling
async fn looks_like_layer_file(path: &Utf8Path) -> bool {
let mut magic = [0u8; 1];
tokio::fs::File::open(path).await.ok()
.and_then(|mut f| std::io::Read::read_exact(&mut f, &mut magic).ok())
.is_some()
} Try / catch
// main.rs already tries pg control first; keep the same fallback shape in wrappers
if let Err(e) = print_layerfile(&path).await {
if e.to_string().starts_with("not an image or delta layer") {
// try the next candidate format or re-download the layer from remote storage
re_download_and_retry(&path).await?;
} else {
return Err(e);
}
} Prevention
- Verify file identity and size before running layer tools; wrong files are the most common cause.
- Re-download layers from remote storage when truncation is possible.
- Keep the ctl version aligned with the pageserver that wrote the layer files.
When it happens
Trigger: Passing a path that is not a layer file (for example index_part.json or a pg control file), a truncated download, or a layer written by a pageserver with a different layer file version.
Common situations: Wrong file passed on the command line; interrupted downloads from remote storage; ctl and pageserver built from different versions with changed on-disk formats.
Related errors
- 'REMOTE_STORAGE_CONFIG' environment variable must be set to
- unsupported linekind: {s}
- unexpected in-memory layer
- 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/6b72f84765825861.
Report an issue: GitHub.