{"record":{"id":"31183ed82dc6c602","repo":"grafana/k6","slug":"uploading-archive-w","errorCode":null,"errorMessage":"uploading archive: %w","messagePattern":"uploading archive: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/cloudapi/provisioning/api.go","lineNumber":107,"sourceCode":"\tPushPeriodSeconds string\n\tMessageMaxSize    int32\n\tAllowedLabels     []string\n}\n\n// UploadArchive PUTs pre-serialised archive bytes to the given\n// presigned S3 URL. The URL carries auth in query params, so no\n// Authorization header is set. Retries on 5xx and transport errors.\nfunc (c *Client) UploadArchive(ctx context.Context, uploadURL string, body []byte) error {\n\treq, err := http.NewRequestWithContext(ctx, http.MethodPut, uploadURL, bytes.NewReader(body))\n\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}","sourceCodeStart":89,"sourceCodeEnd":125,"githubUrl":"https://github.com/grafana/k6/blob/01ffac6f245854c1b8adc6a69857c76a15f90022/internal/cloudapi/provisioning/api.go#L89-L125","documentation":"Returned by Client.UploadArchive (internal/cloudapi/provisioning/api.go:107) when doWithRetry gives up on the PUT to the presigned S3 URL. doWithRetry already retries transport errors and 5xx responses, so seeing this error means the request never produced a usable HTTP response even after those retries: DNS resolution failure, connection refused/reset, TLS handshake failure, or a context cancellation/timeout. The 4xx responses (like an expired signature) do NOT produce this error — they surface as 'archive upload failed: %d %s'.","triggerScenarios":"PUT to the presigned URL failing at transport level: no DNS for the S3 endpoint, firewall/proxy blocking the upload host, TLS interception breaking the handshake, request context cancelled (e.g. global timeout or Ctrl+C) mid-retry, or S3 returning only 5xx across all retry attempts.","commonSituations":"Corporate egress proxies blocking *.amazonaws.com; container with broken DNS; k6_CLOUD_TIMEOUT-style overall context too short for a large archive; transient S3 outage persisting longer than the retry budget; VPN half-open connections.","solutions":["Inspect the wrapped error: a net.DNSError means DNS, a context.Canceled/DeadlineExceeded means the run context died, TLS x509 errors point at interception — fix that layer first.","Verify egress to the S3 host in the URL (curl -I on scheme+host) from the same machine/container and proxy env.","Retry the whole k6 run — a new StartLocalExecution issues a fresh presigned URL and a new upload attempt.","For very large archives on slow links, raise the client timeout passed to provisioning.NewClient so the context outlives the upload.","If S3 is returning persistent 5xx, wait and retry later; nothing client-side fixes it."],"exampleFix":"// before\nif err := c.UploadArchive(ctx, resp.ArchiveUploadURL, body); err != nil { return err }\n\n// after — surface the cause class and retry once with a fresh presigned URL\nerr := c.UploadArchive(ctx, uploadURL, body)\nif err != nil {\n\tvar dnsErr *net.DNSError\n\tswitch {\n\tcase errors.As(err, &dnsErr):\n\t\treturn fmt.Errorf(\"cannot resolve archive upload host %q: %w\", dnsErr.Name, err)\n\tcase errors.Is(err, context.DeadlineExceeded):\n\t\treturn fmt.Errorf(\"archive upload timed out (archive size %d bytes): %w\", len(body), err)\n\t}\n}","handlingStrategy":"retry","validationCode":"// Pre-flight reachability of the upload host (cheap TCP probe) before the PUT.\nu, err := url.Parse(uploadURL)\nif err != nil {\n\treturn err\n}\nconn, err := net.DialTimeout(\"tcp\", u.Host+portOf(u), 5*time.Second)\nif err != nil {\n\treturn fmt.Errorf(\"archive upload host unreachable: %w\", err)\n}\n_ = conn.Close()","typeGuard":null,"tryCatchPattern":"var err error\nfor attempt := 1; attempt <= 3; attempt++ {\n\terr = c.UploadArchive(ctx, uploadURL, body)\n\tif err == nil {\n\t\tbreak\n\t}\n\tif errors.Is(err, context.Canceled) || errors.Is(err, context.DeadlineExceeded) {\n\t\tbreak // retrying cannot help a dead context\n\t}\n\t// transport errors are worth retrying with a fresh URL\n\tuploadURL = refreshPresignedURL()\n\ttime.Sleep(time.Duration(attempt) * 5 * time.Second)\n}","preventionTips":["Ensure egress to the S3 host in the presigned URL is allowed (proxy/firewall allowlist).","Size the client timeout to the archive size and link speed before starting a run.","Upload immediately after StartLocalExecution so the presigned window is fresh and the retry budget is intact."],"tags":["k6","cloud","provisioning","s3","upload","network","retry-exhausted"],"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"}