windmill-labs/windmill · error

Failed to read repository archive: {e}

Error message

Failed to read repository archive: {e}

What it means

While streaming a repository archive download to disk chunk by chunk, one HTTP chunk failed to arrive (reqwest error wrapped in anyhow). The download is aborted so a partial archive is never extracted.

Source

Thrown at backend/windmill-worker/src/ansible_executor.rs:483

            return Err(error::Error::BadRequest(format!(
                "Failed to download `{}` ({}): {}",
                resource, status, body
            )));
        }

        let commit = response
            .headers()
            .get("x-commit-sha")
            .and_then(|v| v.to_str().ok())
            .unwrap_or("unknown")
            .to_string();

        // Written out chunk by chunk: a repository is arbitrarily large, and
        // holding one in the worker's memory would take every job on it down.
        let mut file = tokio::fs::File::create(&download_archive).await?;
        let mut stream = response.bytes_stream();
        while let Some(chunk) = stream.next().await {
            let chunk = chunk.map_err(|e| anyhow!("Failed to read repository archive: {e}"))?;
            file.write_all(&chunk).await?;
        }
        file.flush().await?;
        drop(file);

        // Dropping the join handle detaches the blocking task rather than
        // stopping it, so the flag is what a cancelled job uses to reach the
        // extraction loop. The guard sets it when this future is dropped.
        let aborted = std::sync::Arc::new(std::sync::atomic::AtomicBool::new(false));
        let _abort_on_drop = AbortOnDrop(aborted.clone());
        let unpack_archive = download_archive.clone();
        tokio::task::spawn_blocking(move || {
            unpack_repo_archive(&unpack_archive, &download_target, &aborted)
        })
        .await
        .map_err(|e| anyhow!("Failed to extract repository archive: {e}"))??;

        Ok(commit)

View on GitHub (pinned to e474e8803c)

Solutions

  1. Retry the job — the code deletes partial state on failure
  2. Check network stability/proxy timeouts between worker and the git host
  3. Reduce archive size (shallow/subset repos) if the host truncates large transfers
  4. Verify the archive URL is still valid and the token hasn't expired mid-transfer
Defensive patterns

Strategy: retry

Validate before calling

curl -sSI "$ARCHIVE_URL" | head -n1  # check reachability before streaming
df -h "$DOWNLOAD_DIR"                  # ensure disk space

Try / catch

let mut attempts = 0;
loop {
    match fetch_repo_archive(...).await {
        Err(e) if e.to_string().contains("Failed to read repository archive") && attempts < 2 => {
            attempts += 1;
            tokio::time::sleep(Duration::from_secs(5 * attempts)).await;
        }
        other => { other?; break; }
    }
}

Prevention

When it happens

Trigger: Network interruption, proxy timeout, or the git/archive server closing the connection mid-stream while the worker streams response.bytes_stream().

Common situations: Large repos over flaky networks; corporate proxies with short idle timeouts; git host rate-limiting or resetting connections.

Related errors


AI-assisted analysis of windmill-labs/windmill@e474e8803c (2026-09-03). Data as JSON: /api/errors/5c237fa25da03ab3. Report an issue: GitHub.