grafana/k6 · error

parsing

Error message

parsing %s: %w

What it means

Returned while the k6 browser module initializes (browser/module.go:127) when the K6_BROWSER_SCREENSHOTS_OUTPUT environment variable is set but its value cannot be parsed as a presigned-URL config. Any of the specific parse failures (bad k=v format, bad header, invalid option, invalid url, or 'missing required url') bubbles up wrapped as 'parsing K6_BROWSER_SCREENSHOTS_OUTPUT: ...'. Because it happens at module init, the whole test run aborts before any script executes.

Solutions

  1. Fix the value to the documented format: url=<absolute-url>,basePath=<dir>,header.<name>=<value> (url= is required; headers/basePath optional)
  2. Unset K6_BROWSER_SCREENSHOTS_OUTPUT to fall back to saving screenshots on local disk (LocalFilePersister)
  3. Echo the variable in your shell/CI before k6 run to catch paste errors

Example fix

# before
export K6_BROWSER_SCREENSHOTS_OUTPUT='https://cdn.example.com/upload,base=/shots'
# after
export K6_BROWSER_SCREENSHOTS_OUTPUT='url=https://cdn.example.com/upload,basePath=/shots,header.1=auth-token'
Defensive patterns

Strategy: validation

Validate before calling

#!/usr/bin/env node
const v = process.env.K6_BROWSER_SCREENSHOTS_OUTPUT;
if (v !== undefined && v !== '') {
  const parts = v.split(',');
  const shapeOk = parts.every(p => p.split('=').length === 2);
  const keysOk = parts.every(p => /^(url|basePath|header\.[^,.]+)$/.test(p.split('=')[0]));
  const hasUrl = parts.some(p => p.startsWith('url='));
  if (!(shapeOk && keysOk && hasUrl)) {
    console.error('K6_BROWSER_SCREENSHOTS_OUTPUT must be url=<abs-url>[,basePath=..][,header.k=v]');
    process.exit(1);
  }
}

Prevention

When it happens

Trigger: Exporting K6_BROWSER_SCREENSHOTS_OUTPUT with a malformed value: missing the required url= segment, a segment without '=', an unknown key like ulr=, or a bad header entry — e.g. K6_BROWSER_SCREENSHOTS_OUTPUT='basePath=/shots,header.1=a'.

Common situations: Configuring remote screenshot upload in CI (exporting the var in a pipeline) with a typo'd or partially-pasted value; setting the var to a bare URL without the url= prefix; leftover experimental values from another tool.

Related errors


AI-assisted analysis of grafana/k6@01ffac6f24 (2026-08-18). Data as JSON: /api/errors/1840f29d4fb8b381. Report an issue: GitHub.

Appendix: source

Thrown at internal/js/modules/k6/browser/browser/file_persister.go:30

type presignedURLConfig struct {
	getterURL string
	headers   map[string]string
	basePath  string
}

// newScreenshotPersister will return either a persister that persists file to the local
// disk or uploads the files to a remote location. This decision depends on whether
// the K6_BROWSER_SCREENSHOTS_OUTPUT env var is setup with the correct configs.
func newScreenshotPersister(envLookup env.LookupFunc) (filePersister, error) {
	envVar, ok := envLookup(env.ScreenshotsOutput)
	if !ok || envVar == "" {
		return &storage.LocalFilePersister{}, nil
	}

	popts, err := parsePresignedURLEnvVar(envVar)
	if err != nil {
		return nil, fmt.Errorf("parsing %s: %w", env.ScreenshotsOutput, err)
	}

	return storage.NewRemoteFilePersister(popts.getterURL, popts.headers, popts.basePath), nil
}

// parsePresignedURLEnvVar will parse a value such as:
// url=https://127.0.0.1/,basePath=/screenshots,header.1=a,header.2=b
// and return them.
//

func parsePresignedURLEnvVar(envVarValue string) (presignedURLConfig, error) {
	ss := strings.Split(envVarValue, ",")

	presignedURL := presignedURLConfig{
		headers: make(map[string]string),
	}
	for _, s := range ss {
		// The key value pair should be of the form key=value, so split

View on GitHub (pinned to 01ffac6f24)