{"record":{"id":"9a3585d5c57d2849","repo":"grafana/k6","slug":"archive-upload-failed-d-s","errorCode":null,"errorMessage":"archive upload failed: %d %s","messagePattern":"archive upload failed: (.+?) (.+?)","errorType":"http","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/cloudapi/provisioning/api.go","lineNumber":117,"sourceCode":"\tif err != nil {\n\t\treturn fmt.Errorf(\"creating upload request: %w\", err)\n\t}\n\treq.Header.Set(\"Content-Type\", \"application/x-tar\")\n\treq.ContentLength = int64(len(body))\n\n\tresp, err := c.doWithRetry(req)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"uploading archive: %w\", err)\n\t}\n\tdefer func() {\n\t\t_, _ = io.Copy(io.Discard, resp.Body)\n\t\t_ = resp.Body.Close()\n\t}()\n\n\tif resp.StatusCode < 200 || resp.StatusCode >= 300 {\n\t\trespBody, readErr := io.ReadAll(resp.Body)\n\t\tif readErr != nil || len(respBody) == 0 {\n\t\t\treturn fmt.Errorf(\"archive upload failed: %d %s\",\n\t\t\t\tresp.StatusCode, http.StatusText(resp.StatusCode))\n\t\t}\n\t\treturn fmt.Errorf(\"archive upload failed: %d %s: %s\",\n\t\t\tresp.StatusCode, http.StatusText(resp.StatusCode), respBody)\n\t}\n\n\treturn nil\n}\n\n// WaitForTestRunReady polls GET /cloud/v6/test_runs/{id} until the\n// backend status becomes \"initializing\" (signalling that k6 can begin\n// local execution). Returns an error if the test run reaches \"aborted\"\n// or \"completed\" first. Cancellable via context. Logs status transitions\n// at Debug level once per change.\n//\n// If pollInterval is <= 0 the defaultWaitPollInterval is used.\nfunc (c *Client) WaitForTestRunReady(ctx context.Context, testRunID int64, pollInterval time.Duration) error {\n\tif pollInterval <= 0 {","sourceCodeStart":99,"sourceCodeEnd":135,"githubUrl":"https://github.com/grafana/k6/blob/93accf6570dcd306ca5e99cc44c393ee3797761b/internal/cloudapi/provisioning/api.go#L99-L135","documentation":"The presigned PUT returned a non-2xx status and the response body was empty or unreadable, so UploadArchive can only report the numeric code and reason phrase (internal/cloudapi/provisioning/api.go:113-118). The upload was rejected by the storage service or something in front of it, but the error carries no server-side detail to narrow it down.","triggerScenarios":"403 with empty body from an expired presigned token (some providers and load balancers omit the XML); 400/405 from gateways that mishandle PUT or query-string auth; 5xx error pages with zero-length bodies.","commonSituations":"Delays between provisioning and upload (script setup, slow archive build) outliving the URL's validity; clock skew on the runner invalidating the signature window; middleboxes answering before the request reaches storage.","solutions":["Act on the status code: 403 usually means the presigned URL expired - re-run to get a fresh one; 400/405 suggests an intermediary mishandling the PUT","Verify the runner's clock is NTP-synced - signature windows are time-sensitive","Minimize the gap between start_local_execution and the upload (avoid long setup steps before upload)","Capture the exchange with a debugging proxy to see who actually answered"],"exampleFix":null,"handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"if err := c.UploadArchive(ctx, url, body); err != nil {\n    var status int\n    if _, e := fmt.Sscanf(err.Error(), \"archive upload failed: %d\", &status); e == nil && status == 403 {\n        // likely expired presigned URL: re-provision the run for a fresh one instead of reusing this URL\n    }\n}","preventionTips":["Start the upload promptly after provisioning - presigned URLs have short lifetimes","Keep runner clocks NTP-synced; skewed clocks invalidate time-window signatures","Log the numeric status when it occurs - 403 vs 400 vs 5xx points to expiry vs rewriting vs outage"],"tags":["cloud","s3","upload","archive","http","provisioning"],"backgroundTag":null,"analyzedSha":"93accf6570dcd306ca5e99cc44c393ee3797761b","analyzedAt":"2026-08-15T21:23:27.118Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}