grafana/k6 · error
archive upload failed: %d %s: %s
Error message
archive upload failed: %d %s: %s
What it means
Same rejection path as the empty-body variant, but here the response body was readable and is appended verbatim (internal/cloudapi/provisioning/api.go:119-122). For S3-style storage this is typically an XML error document whose <Code> element names the exact cause - ExpiredToken, AccessDenied, SignatureDoesNotMatch, EntityTooLarge - making this the most diagnostic of the upload failures.
Source
Thrown at internal/cloudapi/provisioning/api.go:120
req.Header.Set("Content-Type", "application/x-tar")
req.ContentLength = int64(len(body))
resp, err := c.doWithRetry(req)
if err != nil {
return fmt.Errorf("uploading archive: %w", err)
}
defer func() {
_, _ = io.Copy(io.Discard, resp.Body)
_ = resp.Body.Close()
}()
if resp.StatusCode < 200 || resp.StatusCode >= 300 {
respBody, readErr := io.ReadAll(resp.Body)
if readErr != nil || len(respBody) == 0 {
return fmt.Errorf("archive upload failed: %d %s",
resp.StatusCode, http.StatusText(resp.StatusCode))
}
return fmt.Errorf("archive upload failed: %d %s: %s",
resp.StatusCode, http.StatusText(resp.StatusCode), respBody)
}
return nil
}
// WaitForTestRunReady polls GET /cloud/v6/test_runs/{id} until the
// backend status becomes "initializing" (signalling that k6 can begin
// local execution). Returns an error if the test run reaches "aborted"
// or "completed" first. Cancellable via context. Logs status transitions
// at Debug level once per change.
//
// If pollInterval is <= 0 the defaultWaitPollInterval is used.
func (c *Client) WaitForTestRunReady(ctx context.Context, testRunID int64, pollInterval time.Duration) error {
if pollInterval <= 0 {
pollInterval = defaultWaitPollInterval
}
View on GitHub (pinned to 93accf6570)
Solutions
- Read the <Code> in the appended body: ExpiredToken -> re-run for a fresh URL; SignatureDoesNotMatch -> something altered the URL in transit (bypass the proxy); AccessDenied -> backend/bucket policy, contact cloud support; EntityTooLarge -> shrink the archive
- Shrink the archive by removing unnecessary bundled files or using --include-only-env? or k6 archive optimizations; check archive size before upload
- Re-run the test - each provisioning issues a fresh presigned URL
- If the backend is yours, raise the upload size limit or URL lifetime
Example fix
# before - bundling a huge dataset into the archive cp ./huge-fixtures/** ./ # tens of MB k6 cloud run --local-execution script.js # archive upload failed: 400 ... EntityTooLarge # after - keep the archive lean; fetch data at runtime # (remove fixtures from the bundle) k6 cloud run --local-execution script.js
Defensive patterns
Strategy: retry
Type guard
// classify S3-style XML bodies embedded in the error
func uploadErrCode(errMsg string) string {
m := regexp.MustCompile(`<Code>([A-Za-z]+)</Code>`).FindStringSubmatch(errMsg)
if m == nil {
return ""
}
return m[1]
} Try / catch
if err := c.UploadArchive(ctx, url, body); err != nil {
switch uploadErrCode(err.Error()) {
case "ExpiredToken", "AccessDenied":
// re-run the test for a fresh URL / check backend policy - do not blind-retry this URL
case "SignatureDoesNotMatch":
// URL was altered in transit: fix the proxy path first
case "EntityTooLarge":
// shrink the archive before retrying
}
} Prevention
- Keep archives small: exclude datasets and binaries from the tarball; load data at runtime instead
- Bypass URL-rewriting proxies for presigned upload hosts
- Read the appended body first - it names the exact failure and dictates the fix
When it happens
Trigger: SignatureDoesNotMatch after a proxy or client altered query parameters; ExpiredToken when the upload starts after the presigned window closed; AccessDenied from bucket/IAM policy on the backend; EntityTooLarge when the archive exceeds the bucket or backend limit.
Common situations: Corporate proxies normalizing/encoding URLs differently; long-running CI jobs reusing a URL issued minutes earlier; large archives (big bundled assets or many files) exceeding upload limits.
Related errors
- archive upload failed: %d %s
- creating upload request: %w
- uploading archive: %w
- fetching test status: %w
- upload archive: %w
AI-assisted analysis of grafana/k6@93accf6570 (2026-08-15).
Data as JSON: /api/errors/057906c95a3fb913.
Report an issue: GitHub.