grafana/k6 · error
upload archive
Error message
upload archive: %w
What it means
ProvisionLocalExecution could not PUT the serialized archive bytes to the presigned upload URL returned by StartLocalExecution (sleResp.ArchiveUploadURL). The wrapped error is either an HTTP failure from the object store (S3/GCS) or a transport error; the presigned URL is short-lived and bound to the declared ArchiveSize.
Solutions
- Retry provisioning end-to-end to get a fresh presigned URL — do not cache or reuse ArchiveUploadURL
- Make sure the exact same archiveBytes buffer produced during serialization is what gets uploaded (no second Write)
- Allow egress to the object-store domain reported in the error (often S3) in proxy/firewall rules
- Compress or shrink the archive (drop unused data files) if uploads routinely outlive the URL lifetime
Example fix
// before archiveBytes = maybeRewritten // re-serialized or modified after size was computed err := client.UploadArchive(ctx, *sleResp.ArchiveUploadURL, archiveBytes) // after // upload the identical buffer whose length was declared err := client.UploadArchive(ctx, *sleResp.ArchiveUploadURL, archiveBytes)
Defensive patterns
Strategy: retry
Validate before calling
// guard the invariant: declared size must equal uploaded bytes
if sleResp.ArchiveUploadURL != nil && int64(len(archiveBytes)) != sleReq.ArchiveSize {
return nil, fmt.Errorf("archive size drift: declared %d, have %d", sleReq.ArchiveSize, len(archiveBytes))
} Try / catch
err := c.UploadArchive(ctx, *sleResp.ArchiveUploadURL, archiveBytes)
if err != nil {
if isTransientOrExpired(err) {
// re-run provisioning to obtain a fresh presigned URL; do not reuse it
return nil, fmt.Errorf("upload archive (re-provision for new URL): %w", err)
}
return nil, fmt.Errorf("upload archive: %w", err)
} Prevention
- Upload immediately after StartLocalExecution; presigned URLs are short-lived
- Freeze the archive bytes buffer after computing its length and upload that exact slice
- Pre-verify firewall egress to the object-store domain, not just the API host
When it happens
Trigger: Presigned URL expired because too much time passed between StartLocalExecution and the upload, request signature mismatch when the uploaded body length differs from the declared ArchiveSize, network egress blocked to the object store domain, or the URL was already consumed.
Common situations: Slow links or large archives (>1 GB) exceeding URL validity, corporate firewalls allowing the API host but blocking *.amazonaws.com, or code that mutates/re-serializes the archive between computing archiveBytes and uploading it.
Related errors
- archive upload failed
- uploading archive
- archive upload failed
- creating upload request
- missing presigned url in response body
AI-assisted analysis of grafana/k6@01ffac6f24 (2026-08-18).
Data as JSON: /api/errors/e37cf801dbf09aab.
Report an issue: GitHub.
Appendix: source
Thrown at internal/cloudapi/provisioning/provision.go:93
archiveBytes = buf.Bytes()
}
sleReq := StartLocalExecutionRequest{
Options: params.Options,
MaxVUs: params.MaxVUs,
TotalDuration: params.TotalDuration,
ArchiveSize: archiveSize,
}
sleResp, err := c.StartLocalExecution(ctx, loadTestID, sleReq)
if err != nil {
return nil, fmt.Errorf("start local execution: %w", err)
}
switch {
case params.Archive != nil && sleResp.ArchiveUploadURL != nil:
if err := c.UploadArchive(ctx, *sleResp.ArchiveUploadURL, archiveBytes); err != nil {
return nil, fmt.Errorf("upload archive: %w", err)
}
case params.Archive != nil && sleResp.ArchiveUploadURL == nil:
// We had an archive to upload but the API returned no upload URL;
// proceed without uploading rather than failing the run.
c.logger.Warn("archive present but provisioning API returned no upload URL; skipping archive upload")
}
if err := c.WaitForTestRunReady(ctx, sleResp.TestRunID, params.PollInterval); err != nil {
return nil, fmt.Errorf("wait for test run ready: %w", err)
}
return &ProvisionResult{
TestRunID: sleResp.TestRunID,
TestRunDetailsPageURL: sleResp.TestRunDetailsPageURL,
RuntimeConfig: sleResp.RuntimeConfig,
}, nil
}
View on GitHub (pinned to 01ffac6f24)