{"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/01ffac6f245854c1b8adc6a69857c76a15f90022/internal/cloudapi/provisioning/api.go#L99-L135","documentation":"Returned by Client.UploadArchive (internal/cloudapi/provisioning/api.go:117) when the PUT to the presigned S3 URL completed at HTTP level but returned a non-2xx status whose body could not be read (readErr != nil) or was empty. You get the numeric status and status text only — no S3 XML detail. Because doWithRetry already retried 5xx, a non-2xx reaching this branch is typically a 4xx client-side rejection (auth/signature/routing) or an uncommon non-2xx with no body.","triggerScenarios":"S3 (or an intermediary) answering the PUT with e.g. 403 whose body is empty or the connection drops while reading the error body; a proxy returning a bare 400/502 with zero-length body; statuses outside 200-299 after retries finished.","commonSituations":"Presigned URL expired before the upload started (slow start, paused process, retry much later); system clock skew invalidating the signature; corporate proxy rewriting the request so the signature no longer matches; bucket/policy changes making the URL invalid.","solutions":["Map the status: 403 => expired or mismatched presigned signature (most common); 400 => malformed request/proxy rewrite; 5xx with empty body => intermediary stripping bodies.","For 403, re-run the command so a fresh presigned URL is minted; avoid long delays between StartLocalExecution and UploadArchive.","Check system clock synchronization (chrony/ntpd) — signature validation is time-sensitive.","If a proxy is in play, bypass it for the S3 host (NO_PROXY) or verify it does not alter headers/query.","If bodies are being stripped, capture with --http-debug or a packet capture to recover the S3 error detail."],"exampleFix":"// before\nif err := c.UploadArchive(ctx, uploadURL, body); err != nil {\n\treturn err // \"archive upload failed: 403 Forbidden\"\n}\n\n// after — translate the common status for the operator\nif err := c.UploadArchive(ctx, uploadURL, body); err != nil {\n\tif strings.Contains(err.Error(), \"403\") {\n\t\treturn fmt.Errorf(\"archive upload rejected (presigned URL expired or signature mismatch), re-run to mint a new URL: %w\", err)\n\t}\n\treturn err\n}","handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"if err := c.UploadArchive(ctx, uploadURL, body); err != nil {\n\tmsg := err.Error()\n\tswitch {\n\tcase strings.Contains(msg, \"403\"):\n\t\t// expired/mismatched presigned signature → re-run to mint a new URL\n\tcase strings.Contains(msg, \"400\"):\n\t\t// request altered in transit (proxy) → inspect NO_PROXY / header rewriting\n\tdefault:\n\t\t// surface as-is; empty-body non-2xx is rare enough to investigate manually\n\t}\n}","preventionTips":["Keep system clocks synchronized (NTP) so presigned-URL signature windows validate.","Start the upload promptly after provisioning; avoid sleeping between StartLocalExecution and UploadArchive.","Bypass HTTP proxies for the S3 upload host so neither URL nor headers get rewritten."],"tags":["k6","cloud","provisioning","s3","upload","http-status","presigned-url"],"backgroundTag":"s3-upload-failed","analyzedSha":"01ffac6f245854c1b8adc6a69857c76a15f90022","analyzedAt":"2026-08-18T03:05:52.393Z","contentChangedAt":"2026-08-18T03:05:52.393Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}