{"record":{"id":"057906c95a3fb913","repo":"grafana/k6","slug":"archive-upload-failed-d-s-s","errorCode":null,"errorMessage":"archive upload failed: %d %s: %s","messagePattern":"archive upload failed: (.+?) (.+?): (.+?)","errorType":"http","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/cloudapi/provisioning/api.go","lineNumber":120,"sourceCode":"\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 {\n\t\tpollInterval = defaultWaitPollInterval\n\t}\n","sourceCodeStart":102,"sourceCodeEnd":138,"githubUrl":"https://github.com/grafana/k6/blob/01ffac6f245854c1b8adc6a69857c76a15f90022/internal/cloudapi/provisioning/api.go#L102-L138","documentation":"Returned by Client.UploadArchive (internal/cloudapi/provisioning/api.go:120) when the PUT to the presigned S3 URL returned a non-2xx status and the response body was successfully read — the body is appended verbatim, which for S3 is an XML <Error> document naming the exact S3 code (e.g. AccessDenied, RequestTimeoutExpired, EntityTooLarge). This is the most informative of the three upload-failure variants; read the S3 Code element to know precisely why the object write was refused.","triggerScenarios":"PUT answered with 403 + <Error><Code>AccessDenied</Code> (expired/abused presigned URL), 400 + AuthorizationQueryParametersError (tampered query string), 413 + EntityTooLarge (object exceeds bucket/account limit), 408/400 + RequestTimeout (slow upload over the signed window).","commonSituations":"Delay between start_local_execution and the actual PUT exceeding the URL's signing window on slow archive builds; proxies or scripts appending/encoding extra query params; archives above the S3/bucket size policy; clock skew.","solutions":["Locate the <Code>...</Code> value inside the trailing body text — it is the authoritative S3 reason; act on that code first.","AccessDenied/RequestTimeoutExpired: restart the run to get a new presigned URL and upload promptly; minimize work between StartLocalExecution and UploadArchive.","EntityTooLarge: shrink the archive (drop large files from the script directory) or use --no-archive-upload (ArchiveSize 0 path) if scripts are self-contained.","AuthorizationQueryParametersError: ensure nothing (proxy, wrapper, logging) rewrites the URL query; pass uploadURL byte-for-byte.","Persist clock sync on the host to prevent signature window misses."],"exampleFix":"// before\nreturn fmt.Errorf(\"archive upload failed: %d %s: %s\", resp.StatusCode, http.StatusText(resp.StatusCode), respBody)\n\n// after — extract the S3 error code for actionable output\nvar s3code string\nif m := regexp.MustCompile(`<Code>([^<]+)</Code>`).FindSubmatch(respBody); m != nil {\n\ts3code = string(m[1])\n}\nreturn fmt.Errorf(\"archive upload failed: %d %s (s3 code %q): %s\", resp.StatusCode, http.StatusText(resp.StatusCode), s3code, respBody)","handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"if err := c.UploadArchive(ctx, uploadURL, body); err != nil {\n\tif code := s3ErrorCode(err); code != \"\" {\n\t\tswitch code {\n\t\tcase \"AccessDenied\", \"RequestTimeoutExpired\":\n\t\t\t// re-run provisioning for a fresh presigned URL, then retry once\n\t\tcase \"EntityTooLarge\":\n\t\t\t// shrink the archive or use --no-archive-upload; do not retry\n\t\tdefault:\n\t\t\treturn err\n\t\t}\n\t}\n}\n\nfunc s3ErrorCode(err error) string {\n\tm := regexp.MustCompile(`<Code>([^<]+)</Code>`).FindStringSubmatch(err.Error())\n\tif m == nil {\n\t\treturn \"\"\n\t}\n\treturn m[1]\n}","preventionTips":["Keep archives well under 2 GiB and free of large binary assets.","Verify nothing rewrites the URL query (signatures cover query params).","On slow links, prefer smaller archives so uploads finish inside the presigned window."],"tags":["k6","cloud","provisioning","s3","upload","http-status","error-body"],"backgroundTag":"presigned-url-expired","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"}