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

  1. Check the wrapped error: a 404 means the archive path is wrong - compare the requested ext_path against the bucket layout
  2. Verify egress/DNS from the compute pod to remote_ext_base_url (curl the full URL)
  3. Fix the extension version/name so it maps to an archive that exists in the bucket
  4. 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

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


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