windmill-labs/windmill · error

upload_s3_file request to {url} failed with status {status}:

Error message

upload_s3_file request to {url} failed with status {status}: {body}

What it means

upload_s3_file uploads content through the workspace's S3 file API and raises this error for any non-200 response, including the target URL, status code, and response body. Unlike the empty-message errors nearby, this one is fully contextualized.

Source

Thrown at backend/windmill-common/src/client.rs:298

                reqwest::header::ACCEPT,
                reqwest::header::HeaderValue::from_static("application/json"),
            )
            .header(
                reqwest::header::AUTHORIZATION,
                reqwest::header::HeaderValue::from_str(&format!("Bearer {}", self.token))
                    .map_err(|e| anyhow::anyhow!(e.to_string()))?,
            )
            .body(Body::wrap_stream(body))
            .send()
            .await
            .context(format!("Failed to send upload_s3_file request to {url}"))?;

        match response.status().as_u16() {
            200u16 => Ok(()),
            _ => {
                let status = response.status();
                let body = response.text().await.unwrap_or_default();
                Err(anyhow::anyhow!(
                    "upload_s3_file request to {url} failed with status {status}: {body}"
                ))
            }
        }
    }

    pub async fn download_s3_file(
        &self,
        workspace_id: &str,
        file_key: &str,
        storage: Option<String>,
    ) -> anyhow::Result<bytes::Bytes> {
        let mut query = vec![("file_key", file_key.to_string())];
        if let Some(storage) = storage {
            query.push(("storage", storage));
        }
        let response = self
            .force_client

View on GitHub (pinned to e474e8803c)

Solutions

  1. Read the status and body in the message: 404 usually means the route/storage is missing, 413 means size limits, 5xx means upstream storage trouble
  2. Verify workspace large-file/S3 storage configuration in instance settings
  3. Confirm the backend was built with the parquet feature for S3 endpoints
  4. Check the S3/MinIO backend is reachable and credentials valid
  5. Retry transient 5xx with backoff
Defensive patterns

Strategy: try-catch

Validate before calling

// pre-flight: confirm storage configured (workspace large-file/S3 settings) before upload.
if file_len > MAX_UPLOAD { anyhow::bail!("file too large for s3 upload: {file_len}"); }

Try / catch

match client.upload_s3_file(...).await {
    Err(e) if e.to_string().contains("status 413") => anyhow::bail!("file exceeds storage size limit"),
    Err(e) if e.to_string().contains("status 5") => { tokio::time::sleep(Duration::from_secs(2)).await; client.upload_s3_file(...).await }
    other => other,
}

Prevention

When it happens

Trigger: Upload of a file to the S3 storage endpoint returns 4xx/5xx: storage not configured for the workspace (400/404), file too large or quota exceeded (413), invalid s3 path or bad request (400), auth failure (401/403), or upstream S3/MinIO errors surfaced as 5xx.

Common situations: Workspace large-file storage misconfigured or pointing at an unreachable MinIO/S3, missing parquet feature so the route is absent, uploading during a storage outage, wrong resource path used for the S3 bucket.

Related errors


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