{"record":{"id":"1840f29d4fb8b381","repo":"grafana/k6","slug":"parsing-s-w","errorCode":null,"errorMessage":"parsing %s: %w","messagePattern":"parsing (.+?): %w","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/js/modules/k6/browser/browser/file_persister.go","lineNumber":30,"sourceCode":"\ntype presignedURLConfig struct {\n\tgetterURL string\n\theaders   map[string]string\n\tbasePath  string\n}\n\n// newScreenshotPersister will return either a persister that persists file to the local\n// disk or uploads the files to a remote location. This decision depends on whether\n// the K6_BROWSER_SCREENSHOTS_OUTPUT env var is setup with the correct configs.\nfunc newScreenshotPersister(envLookup env.LookupFunc) (filePersister, error) {\n\tenvVar, ok := envLookup(env.ScreenshotsOutput)\n\tif !ok || envVar == \"\" {\n\t\treturn &storage.LocalFilePersister{}, nil\n\t}\n\n\tpopts, err := parsePresignedURLEnvVar(envVar)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"parsing %s: %w\", env.ScreenshotsOutput, err)\n\t}\n\n\treturn storage.NewRemoteFilePersister(popts.getterURL, popts.headers, popts.basePath), nil\n}\n\n// parsePresignedURLEnvVar will parse a value such as:\n// url=https://127.0.0.1/,basePath=/screenshots,header.1=a,header.2=b\n// and return them.\n//\n\nfunc parsePresignedURLEnvVar(envVarValue string) (presignedURLConfig, error) {\n\tss := strings.Split(envVarValue, \",\")\n\n\tpresignedURL := presignedURLConfig{\n\t\theaders: make(map[string]string),\n\t}\n\tfor _, s := range ss {\n\t\t// The key value pair should be of the form key=value, so split","sourceCodeStart":12,"sourceCodeEnd":48,"githubUrl":"https://github.com/grafana/k6/blob/93accf6570dcd306ca5e99cc44c393ee3797761b/internal/js/modules/k6/browser/browser/file_persister.go#L12-L48","documentation":"Wraps any failure from parsing the K6_BROWSER_SCREENSHOTS_OUTPUT environment variable (file_persister.go:30). When this env var is set, the k6 browser module builds a remote screenshot persister by parsing a comma-separated k=v list; if the value violates the expected grammar, module initialization fails with this error and the browser test does not start.","triggerScenarios":"Running a browser test with K6_BROWSER_SCREENSHOTS_OUTPUT set to a malformed value, e.g. 'basepath=/shots' (missing url=), 'url' (no =), 'url=https://h/?s=1' (extra = in query), or an unknown key. Any of the inner errors (421-425, 'missing required url') is re-wrapped as 'parsing K6_BROWSER_SCREENSHOTS_OUTPUT: ...'.","commonSituations":"Teams wiring screenshot upload to presigned S3/GCS endpoints; CI pipelines injecting the env var with quoting or escaping mistakes; values pasted from cloud consoles containing '=' or ',' in query strings; typo'd keys like 'URL=' or 'headers.1=' after renaming from an older k6 version.","solutions":["Read the wrapped cause after the colon: it is one of 'format of value must be k=v', 'format of header must be header.k=v', 'empty header key', 'invalid url', 'invalid option', or 'missing required url'","Fix the value to the exact grammar: url=https://host/,basePath=/screenshots,header.Name=value (lowercase keys, one '=' per segment)","If the URL must contain '=' or ',' (presigned signatures), URL-encode them as %3D and %2C or move them into header.* entries","Unset K6_BROWSER_SCREENSHOTS_OUTPUT entirely to fall back to local disk persistence and confirm the rest of the test works","Re-run k6 after each change; the error appears at browser module startup, before any test code executes"],"exampleFix":"# before\nexport K6_BROWSER_SCREENSHOTS_OUTPUT=\"basepath=/screensots\"\n# after\nexport K6_BROWSER_SCREENSHOTS_OUTPUT=\"url=https://uploads.example.com/,basePath=/screenshots,header.Authorization=Bearer%20tok\"","handlingStrategy":"validation","validationCode":"#!/usr/bin/env bash\n# pre-flight check before `k6 run`\nv=\"${K6_BROWSER_SCREENSHOTS_OUTPUT:?unset}\"\nurl_seen=0\nIFS=',' read -ra segs <<< \"$v\"\nfor s in \"${segs[@]}\"; do\n  k=\"${s%%=*}\"\n  case \"$k\" in\n    url) url_seen=1 ;;\n    basePath|header.*.[^.]*) : ;;\n    *) echo \"invalid option: $k\" >&2; exit 1 ;;\n  esac\n  [ \"$(awk -F= '{print NF}' <<< \"$s\")\" -eq 2 ] || { echo \"segment must be k=v: $s\" >&2; exit 1; }\ndone\n[ \"$url_seen\" -eq 1 ] || { echo \"missing required url\" >&2; exit 1; }","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Treat K6_BROWSER_SCREENSHOTS_OUTPUT as code: keep it in one CI variable and lint it with the pre-flight script before every run","Always include url=https://host/ - it is the only required key","URL-encode '=' (%3D) and ',' (%2C) inside values","Add a smoke test that runs a 1-iteration browser script taking a screenshot whenever the env var changes"],"tags":["k6","browser","configuration","environment-variable","screenshots"],"backgroundTag":null,"analyzedSha":"93accf6570dcd306ca5e99cc44c393ee3797761b","analyzedAt":"2026-08-15T21:23:27.118Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}