neondatabase/neon · error

Received failure response for copy from {from} to {to}.

Error message

Received failure response for copy from {from} to {to}.

What it means

CopyStatus::Failed from Azure's async Copy Blob: the copy was attempted but the service could not read from the source. Typical causes: no read permission on the source (SAS missing read scope), the source was deleted before the copy service read it, or the source blob is in the Archive access tier (archived blobs cannot be copied without rehydration).

Source

Thrown at libs/remote_storage/src/azure_blob.rs:906

            let source_url = format!(
                "{}/{}",
                self.client.url()?,
                self.relative_path_to_name(from)
            );

            let builder = blob_client.copy(Url::from_str(&source_url)?);
            let copy = builder.into_future();

            let result = copy.await?;

            copy_status = Some(result.copy_status);
            loop {
                match copy_status.as_ref().expect("we always set it to Some") {
                    CopyStatus::Aborted => {
                        anyhow::bail!("Received abort for copy from {from} to {to}.");
                    }
                    CopyStatus::Failed => {
                        anyhow::bail!("Received failure response for copy from {from} to {to}.");
                    }
                    CopyStatus::Success => return Ok(()),
                    CopyStatus::Pending => (),
                }
                // The copy is taking longer. Waiting a second and then re-trying.
                // TODO estimate time based on copy_progress and adjust time based on that
                tokio::time::sleep(Duration::from_millis(1000)).await;
                let properties = blob_client.get_properties().into_future().await?;
                let Some(status) = properties.blob.properties.copy_status else {
                    tracing::warn!("copy_status for copy is None!, from={from}, to={to}");
                    return Ok(());
                };
                copy_status = Some(status);
            }
        };

        let res = tokio::select! {
            res = op => res,

View on GitHub (pinned to 8f60b04da4)

Solutions

  1. Verify read access on the source: the SAS/identity needs read on the source container or blob, not just write on the destination
  2. Check the source still exists and is not in Archive tier — rehydrate it or copy from a Hot/Cool replica
  3. If the source is gone, propagate failure upstream instead of retrying
  4. For cross-account copies use a source URL that embeds a read-scoped SAS token

Example fix

// before: copy with a bare source URL (requires read on source, fails cross-account)
let builder = blob_client.copy(Url::from_str(&source_url)?);

// after: copy from a source URL carrying a read-scoped SAS
let source_url = format!("{}/{}?{}", src_account_url, src_blob_name, src_read_sas);
let builder = blob_client.copy(Url::from_str(&source_url)?);
Defensive patterns

Strategy: try-catch

Validate before calling

// Verify the source is readable (and not archived) before issuing the server-side copy.
async fn source_copyable(storage: &GenericRemoteStorage, from: &RemotePath, cancel: &CancellationToken) -> anyhow::Result<()> {
    match storage.head_object(from.get_path().to_string(), cancel).await {
        Ok(meta) => {
            // rejections for archived sources are reported as access errors downstream
            let _ = meta;
            Ok(())
        }
        Err(DownloadError::NotFound) => anyhow::bail!("source vanished before copy"),
        Err(e) => Err(e.into()),
    }
}

Try / catch

// Failure is often permanent (permissions/archive): inspect before retrying.
match storage.copy(&from, &to, &cancel).await {
    Ok(()) => Ok(()),
    Err(e) => {
        let msg = format!("{e:#}");
        if msg.contains("Received failure response for copy") {
            // check source access + tier, fix, then a single retry — do not blind-retry
            verify_source_access(&from).await?;
            storage.copy(&from, &to, &cancel).await
        } else {
            Err(e)
        }
    }
}

Prevention

When it happens

Trigger: Copying a source the credentials cannot read (cross-account or cross-container copy without a read-scoped source SAS); source deleted between the copy request and the server reading it; source blob in Archive tier; source lease/retention states preventing reads.

Common situations: Lifecycle policies moving blobs to Archive tier; copying across storage accounts with insufficient SAS scopes; GC deleting source objects concurrently with copy operations.

Related errors


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