{"record":{"id":"d3e99ccaf54fa074","repo":"grafana/k6","slug":"invalid-url-q","errorCode":null,"errorMessage":"invalid url %q","messagePattern":"invalid url %q","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/js/modules/k6/browser/browser/file_persister.go","lineNumber":83,"sourceCode":"\n\t\t\thh := strings.Split(k, \".\")\n\t\t\tif len(hh) != 2 {\n\t\t\t\treturn presignedURLConfig{}, fmt.Errorf(\"format of header must be header.k=v, received %q\", s)\n\t\t\t}\n\n\t\t\tk = hh[0]\n\t\t\thk = hh[1]\n\n\t\t\tif hk == \"\" {\n\t\t\t\treturn presignedURLConfig{}, fmt.Errorf(\"empty header key, received %q\", s)\n\t\t\t}\n\t\t}\n\n\t\tswitch k {\n\t\tcase \"url\":\n\t\t\tu, err := url.ParseRequestURI(v)\n\t\t\tif err != nil && u.Scheme != \"\" && u.Host != \"\" {\n\t\t\t\treturn presignedURLConfig{}, fmt.Errorf(\"invalid url %q\", s)\n\t\t\t}\n\t\t\tpresignedURL.getterURL = v\n\t\tcase \"basePath\":\n\t\t\tpresignedURL.basePath = v\n\t\tcase \"header\":\n\t\t\tpresignedURL.headers[hk] = hv\n\t\tdefault:\n\t\t\treturn presignedURLConfig{}, fmt.Errorf(\"invalid option %q\", k)\n\t\t}\n\t}\n\n\tif presignedURL.getterURL == \"\" {\n\t\treturn presignedURLConfig{}, errors.New(\"missing required url\")\n\t}\n\n\treturn presignedURL, nil\n}\n","sourceCodeStart":65,"sourceCodeEnd":101,"githubUrl":"https://github.com/grafana/k6/blob/01ffac6f245854c1b8adc6a69857c76a15f90022/internal/js/modules/k6/browser/browser/file_persister.go#L65-L101","documentation":"Intended to be returned when the url= value of K6_BROWSER_SCREENSHOTS_OUTPUT fails url.ParseRequestURI. The guard is 'err != nil && u.Scheme != \"\" && u.Host != \"\"', but Go's ParseRequestURI returns a nil *url.URL on failure, so in practice this branch almost never fires and malformed URLs are often accepted silently. When it does fire (or via the related nil hazard), the fix is the same: supply a valid absolute http/https URL.","triggerScenarios":"Setting url= to a non-URL like 'url=localhost:9200/upload' or 'url=/upload' — values that fail request-URI parsing. Most such inputs are silently stored rather than rejected, surfacing later as upload failures.","commonSituations":"Omitting the scheme (example.com instead of https://example.com) in CI env vars; using a host:port value without a scheme; the error usually appears only in edge cases due to the guard logic, so verify URLs manually.","solutions":["Use a fully-qualified absolute URL: url=https://upload.example.com/","Verify the URL independently (curl -I) before putting it in the env var, since the parser's validation is loose","If you maintain this code, note the guard should be err != nil && (u == nil || ...) — as written the message is nearly unreachable"],"exampleFix":"# before\nexport K6_BROWSER_SCREENSHOTS_OUTPUT='url=cdn.example.com/upload'\n# after\nexport K6_BROWSER_SCREENSHOTS_OUTPUT='url=https://cdn.example.com/upload'","handlingStrategy":"validation","validationCode":"const v = process.env.K6_BROWSER_SCREENSHOTS_OUTPUT ?? '';\nconst urlPart = v.split(',').find(p => p.startsWith('url='));\nif (urlPart) {\n  let u;\n  try { u = new URL(urlPart.slice(4)); } catch { u = null; }\n  if (!u || (u.protocol !== 'http:' && u.protocol !== 'https:')) {\n    throw new Error('url= must be an absolute http(s) URL with scheme and host');\n  }\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Always include the scheme: https://… not bare hostnames","Validate the URL with new URL() or curl before the run — the Go-side guard is loose and rarely fires","Treat remote-upload failures as possible fallout from an unvalidated URL that slipped through"],"tags":["k6","browser","screenshots","environment-variable","url-parsing"],"backgroundTag":"invalid-env-var-format","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"}