{"record":{"id":"18f15771815b9a33","repo":"grafana/k6","slug":"unexpected-http-error-from-s-d-s","errorCode":null,"errorMessage":"unexpected HTTP error from %s: %d %s","messagePattern":"unexpected HTTP error from (.+?): (.+?) (.+?)","errorType":"http","errorClass":null,"httpStatus":null,"severity":"error","filePath":"cloudapi/client.go","lineNumber":211,"sourceCode":"\t}\n\n\tif c := r.StatusCode; c >= 200 && c <= 299 {\n\t\treturn nil\n\t}\n\n\tdata, err := io.ReadAll(r.Body)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tvar payload struct {\n\t\tError ResponseError `json:\"error\"`\n\t}\n\tif err := json.Unmarshal(data, &payload); err != nil {\n\t\tif classified := httperr.ClassifyStatus(r.StatusCode); classified != nil {\n\t\t\treturn classified\n\t\t}\n\t\treturn fmt.Errorf(\n\t\t\t\"unexpected HTTP error from %s: %d %s\",\n\t\t\tr.Request.URL,\n\t\t\tr.StatusCode,\n\t\t\thttp.StatusText(r.StatusCode),\n\t\t)\n\t}\n\tpayload.Error.Response = r\n\treturn payload.Error\n}\n\nfunc shouldRetry(resp *http.Response, err error, attempt, maxAttempts int) bool {\n\tif attempt >= maxAttempts {\n\t\treturn false\n\t}\n\n\tif resp == nil || err != nil {\n\t\treturn true\n\t}","sourceCodeStart":193,"sourceCodeEnd":229,"githubUrl":"https://github.com/grafana/k6/blob/93accf6570dcd306ca5e99cc44c393ee3797761b/cloudapi/client.go#L193-L229","documentation":"CheckResponse (cloudapi/client.go:190-220) wraps every non-2xx response from the cloud API. It first tries to parse the body as {\"error\":...} JSON, then falls back to httperr.ClassifyStatus for well-known statuses; when both fail you get this generic message carrying the request URL, numeric status, and reason phrase. It signals an error response the client cannot interpret - typically a non-JSON error page from something between k6 and the API.","triggerScenarios":"Any cloudapi request (login, create test run, push) where an intermediary returns an unusual status with an HTML/plain body: nginx 502/504 pages, a WAF block page, a captive portal, or K6_CLOUD_HOST pointing at a service that is not the k6 cloud API (e.g. the web app instead of the API host).","commonSituations":"Corporate egress proxies or SSL-inspection appliances rewriting responses; K6_CLOUD_HOST typo ( Hits the Grafana UI host instead of the API ); an outage page served with a status httperr does not classify.","solutions":["Reproduce with curl -v against the exact URL printed in the error and inspect the raw response","Verify K6_CLOUD_HOST is the API endpoint, not the web UI (re-run 'k6 cloud login' to reset it)","Check HTTP_PROXY/HTTPS_PROXY/NO_PROXY and try bypassing the proxy for the cloud host","If the status is 5xx from the cloud itself, check the Grafana Cloud status page and retry"],"exampleFix":"# before\nexport K6_CLOUD_HOST=https://k6.io   # web site, not the API\nk6 cloud run script.js   # unexpected HTTP error from https://k6.io/...: 404 Not Found\n\n# after\nexport K6_CLOUD_HOST=https://api.cloud.k6.io\nk6 cloud run script.js","handlingStrategy":"retry","validationCode":"# pre-flight: assert the configured host answers like the cloud API, not a proxy page\ncurl -sS -o /dev/null -w '%{http_code} %{content_type}\\n' \"$K6_CLOUD_HOST/v1/test-runs\" -H \"Authorization: Token $K6_CLOUD_TOKEN\"","typeGuard":null,"tryCatchPattern":"err := client.Do(req, &response)\nif err != nil {\n    var re *cloudapi.ResponseError\n    if errors.As(err, &re) {\n        // structured cloud error: inspect re.Response.StatusCode\n    } else if strings.HasPrefix(err.Error(), \"unexpected HTTP error from\") {\n        // unclassified/proxy response: retry with backoff, then surface URL+status to the operator\n    }\n}","preventionTips":["Verify K6_CLOUD_HOST points at the API endpoint, never the web UI or a portal","Add the cloud API host to NO_PROXY when an intercepting corporate proxy is in play","Reproduce any occurrence with curl -v before changing k6 configuration"],"tags":["cloud","network","proxy","http","configuration"],"backgroundTag":null,"analyzedSha":"93accf6570dcd306ca5e99cc44c393ee3797761b","analyzedAt":"2026-08-15T21:23:27.118Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}