{"record":{"id":"a326423e69b0ab46","repo":"grafana/k6","slug":"missing-presigned-url-in-response-body","errorCode":null,"errorMessage":"missing presigned url in response body","messagePattern":"missing presigned url in response body","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/js/modules/k6/browser/storage/file_persister.go","lineNumber":195,"sourceCode":"\tbb, err := json.Marshal(b)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"marshaling request body: %w\", err)\n\t}\n\n\treturn bb, nil\n}\n\nfunc readPresignedURLResponse(resp *http.Response) (PresignedURLResponse, error) {\n\tvar rb PresignedURLResponse\n\n\tdecoder := json.NewDecoder(resp.Body)\n\terr := decoder.Decode(&rb)\n\tif err != nil {\n\t\treturn PresignedURLResponse{}, fmt.Errorf(\"decoding response body: %w\", err)\n\t}\n\n\tif len(rb.URLs) == 0 {\n\t\treturn PresignedURLResponse{}, errors.New(\"missing presigned url in response body\")\n\t}\n\n\treturn rb, nil\n}\n\n// newFileUploadRequest creates a new HTTP request to upload a file as a multipart\n// form to the presigned URL received from the server.\nfunc newFileUploadRequest(\n\tctx context.Context,\n\tresp PresignedURLResponse,\n\tdata io.Reader,\n) (*http.Request, error) {\n\t// we don't support multiple presigned URLs at the moment.\n\tpsu := resp.URLs[0]\n\n\t// copy all form fields received from a presigned URL\n\t// response to the multipart form fields.\n\tvar form bytes.Buffer","sourceCodeStart":177,"sourceCodeEnd":213,"githubUrl":"https://github.com/grafana/k6/blob/8d06114777191d21ef885d05c5f8aa9e1ed838ae/internal/js/modules/k6/browser/storage/file_persister.go#L177-L213","documentation":"The browser file persister (file_persister.go) asks the Grafana Cloud (k6 Cloud) service for presigned upload URLs and decodes the JSON body into PresignedURLResponse. readPresignedURLResponse (file_persister.go:189-199) succeeded in decoding the JSON but rb.URLs was empty — the response is well-formed JSON yet contains no presigned urls, so there is no upload destination.","triggerScenarios":"Running k6 with cloud output (--out cloud / cloud execution) where the cloud service (or something in between) returns 200 with a JSON body lacking the urls field: mis-scoped or invalid K6_CLOUD_TOKEN, an API version mismatch between k6 and the service, or a corporate proxy/mock rewriting the response body.","commonSituations":"Expired or wrongly-scoped cloud token; outdated k6 client against a changed cloud API; egress proxies or SSL-inspection middleboxes replacing the body with an error/HTML page that still parses as JSON; intermittent service behavior during incidents.","solutions":["Verify the cloud credentials: K6_CLOUD_TOKEN (or k6 login) valid and scoped for the project","Upgrade k6 to the latest version so the persister matches the current cloud API contract","Capture the actual response body (proxy/debug logs, HTTPS_PROXY inspection) to see what the service really returned","If cloud output is not intended, remove --out cloud / cloud env settings so no persister call happens","If credentials and version check out, contact Grafana support — the service dropped urls for a valid request"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"// Surface credential problems before the run\nimport http from 'k6/http';\nconst res = http.get(`${__ENV.K6_CLOUD_HOST}/v1/authorized`, {\n  headers: { Authorization: `Bearer ${__ENV.K6_CLOUD_TOKEN}` },\n});\nif (res.status !== 200) throw new Error(`cloud auth failed: ${res.status}`);","typeGuard":null,"tryCatchPattern":"// Persister errors abort cloud upload; catch at run level and re-check env\ntry {\n  export const options = { cloud: { projectID: 123 } };\n} catch (e) {\n  if (/presigned url/.test(String(e))) {\n    console.error('cloud returned no upload URLs — check K6_CLOUD_TOKEN and k6 version');\n  }\n}","preventionTips":["Keep K6_CLOUD_TOKEN fresh and correctly scoped","Stay on a current k6 release so the persister matches the cloud API","Watch for proxies/SSL inspection altering cloud response bodies","If cloud output is not needed, remove --out cloud entirely"],"tags":["cloud","upload","presigned-url","browser","json"],"backgroundTag":"presigned-url-missing","analyzedSha":"8d06114777191d21ef885d05c5f8aa9e1ed838ae","analyzedAt":"2026-08-18T03:05:52.393Z","contentChangedAt":"2026-08-18T03:05:52.393Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}