neondatabase/neon · error
error downloading extension {:?}: {:?}
Error message
error downloading extension {:?}: {:?} What it means
The extension download helper fetched {base_url}/{ext_path} via download_extension_tar and the HTTP request failed; the error wraps the transfer error verbatim (DNS failure, connect refused, 404 for a wrong archive path, timeout). There is deliberately no retry yet (see the TODO in the source), so a single transient network hiccup surfaces as a hard error for that extension.
Source
Thrown at compute_tools/src/extension_server.rs:158
panic!("Unsuported postgres version {human_version}");
}
// download the archive for a given extension,
// unzip it, and place files in the appropriate locations (share/lib)
pub async fn download_extension(
ext_name: &str,
ext_path: &RemotePath,
remote_ext_base_url: &Url,
pgbin: &str,
) -> Result<u64> {
info!("Download extension {:?} from {:?}", ext_name, ext_path);
// TODO add retry logic
let download_buffer =
match download_extension_tar(remote_ext_base_url, &ext_path.to_string()).await {
Ok(buffer) => buffer,
Err(error_message) => {
return Err(anyhow::anyhow!(
"error downloading extension {:?}: {:?}",
ext_name,
error_message
));
}
};
let download_size = download_buffer.len() as u64;
info!("Download size {:?}", download_size);
// it's unclear whether it is more performant to decompress into memory or not
// TODO: decompressing into memory can be avoided
let decoder = Decoder::new(download_buffer.as_ref())?;
let mut archive = Archive::new(decoder);
let unzip_dest = pgbin
.strip_suffix("/bin/postgres")
.expect("bad pgbin")
.to_string()View on GitHub (pinned to 8f60b04da4)
Solutions
- Check the wrapped error: a 404 means the archive path is wrong - compare the requested ext_path against the bucket layout
- Verify egress/DNS from the compute pod to remote_ext_base_url (curl the full URL)
- Fix the extension version/name so it maps to an archive that exists in the bucket
- Retry the request or restart the endpoint for transient network failures; better, add retry/backoff around download_extension_tar (the source marks it TODO)
Example fix
// before (compute_tools/src/extension_server.rs) - no retry
let download_buffer = match download_extension_tar(remote_ext_base_url, &ext_path.to_string()).await { ... };
// after: bounded retry for transient failures
let download_buffer = retry(3, Duration::from_secs(2), || async {
download_extension_tar(remote_ext_base_url, &ext_path.to_string()).await
}).await.map_err(|e| anyhow!("error downloading extension {ext_name:?}: {e:?}"))?; Defensive patterns
Strategy: retry
Validate before calling
// Verify the exact artifact URL before download_extension runs
let url = remote_ext_base_url.join(&ext_path.to_string())
.with_context(|| format!("bad ext url {ext_path} base {remote_ext_base_url}"))?;
if reqwest::get(url.clone()).await?.status() == 404 { anyhow::bail!("no such extension archive: {url}"); } Type guard
fn extension_url_resolvable(base: &Url, ext_path: &str) -> bool {
base.join(ext_path).is_ok()
} Try / catch
// Retry transient transfer errors a few times before failing the extension install
for attempt in 0..3 {
match download_extension_tar(remote_ext_base_url, &ext_path.to_string()).await {
Ok(buf) => return Ok(buf),
Err(e) if attempt == 2 => return Err(anyhow!("error downloading extension {ext_name:?}: {e:?}")),
Err(_) => tokio::time::sleep(Duration::from_secs(2u64.pow(attempt as u32))).await,
}
} Prevention
- Pre-check egress/DNS from the compute subnet to the extension bucket
- Keep extension archive naming deterministic (ext/<name>.tar.zst) and covered by tests
- Add the retry/backoff the source TODO asks for so one timeout does not fail an extension install
When it happens
Trigger: download_extension() called with a remote_ext_base_url that is unreachable from the compute, or an ext_path that does not exist on the bucket (404), or the URL join in download_extension_tar produced a malformed URI (bad base/path combination), or the transfer timed out.
Common situations: Wrong extension archive naming (version/platform mismatch, missing ext/ prefix); air-gapped computes without egress to the bucket; DNS/veth issues in the sandbox; transient S3 outage with no retry to absorb it; base URL typo from --remote-ext-base-url.
Related errors
- Remote extensions storage is not configured
- Remote extensions are not configured
- connection to postgres closed
- pageserver connection information should be provided
- safekeeper connstrings should be provided
AI-assisted analysis of neondatabase/neon@8f60b04da4 (2026-08-16).
Data as JSON: /api/errors/149eb45e6fff5664.
Report an issue: GitHub.