grafana/k6 · error
persisting screenshot: %w
Error message
persisting screenshot: %w
What it means
The screenshot was captured successfully, but writing it to disk through the ScreenshotPersister failed. This is a filesystem/IO error on the k6 host side — the bytes in memory are fine, only persistence failed.
Source
Thrown at internal/js/modules/k6/browser/common/screenshotter.go:234
}
// Capture screenshot
buf, err = capture.Do(cdp.WithExecutor(s.ctx, sess))
if err != nil {
return nil, fmt.Errorf("capturing screenshot: %w", err)
}
if shouldSetDefaultBackground {
action := emulation.SetDefaultBackgroundColorOverride()
if err := action.Do(cdp.WithExecutor(s.ctx, sess)); err != nil {
return nil, fmt.Errorf("resetting screenshot background color: %w", err)
}
}
// Save screenshot capture to file
if path != "" {
if err := s.persister.Persist(s.ctx, path, bytes.NewBuffer(buf)); err != nil {
return nil, fmt.Errorf("persisting screenshot: %w", err)
}
}
return buf, nil
}
func getViewPortDimensions(ctx context.Context, sess session, logger *log.Logger) (float64, float64, float64, error) {
visualViewportScale := 1.0
visualViewportPageX, visualViewportPageY := 0.0, 0.0
// Add clip region
//nolint:dogsled
_, _, _, _, cssVisualViewport, _, err := cdppage.GetLayoutMetrics().Do(cdp.WithExecutor(ctx, sess))
if err != nil {
return 0, 0, 0, fmt.Errorf("getting layout metrics for screenshot: %w", err)
}
// we had a null pointer panic cases, when visualViewport is nilView on GitHub (pinned to 93accf6570)
Solutions
- Write to an existing, writable directory — use a relative path like 'screenshots/shot.png' after creating the folder, or '.'.
- Check permissions of the target directory for the user running k6 (and mount a writable volume in Docker).
- If you only need the bytes, drop the path option and handle the returned Buffer yourself.
- Verify disk space on the target filesystem.
Example fix
// before
await page.screenshot({ path: '/var/data/shots/home.png' }); // dir may not exist / no perms
// after (shell): mkdir -p screenshots
await page.screenshot({ path: 'screenshots/home.png' }); Defensive patterns
Strategy: validation
Validate before calling
import { execSync } from 'k6/x/...' // not available; instead verify writability in the shell before the run:
// mkdir -p screenshots && touch screenshots/.probe && rm screenshots/.probe Try / catch
try { await page.screenshot({ path: 'screenshots/s.png' }); }
catch (e) { if (String(e).includes('persisting screenshot')) { const buf = await page.screenshot(); /* use buf */ } else throw e; } Prevention
- Pre-create the screenshots directory in CI before running k6.
- Use relative paths under the (writable) working directory.
- Mount a writable volume when running k6 in Docker.
- Skip the path option if you only need the bytes.
When it happens
Trigger: page.screenshot({path: '...'}) where the path is unwritable: directory does not exist, permission denied, read-only filesystem, path too long, or disk full. Also container setups where the working dir is not writable.
Common situations: Using an absolute path like /home/... or /root/... that the k6 process cannot write; assuming mkdir happens automatically; running k6 in Docker with no mounted writable volume; CI sandboxes with read-only workspaces; Windows path separators in scripts written on Linux.
Related errors
- parsing element handle screenshot options: %w
- converting result (%v of type %t) to size: %w
- getting viewport dimensions: %w
- unpacking window size: %w
- setting screenshot background transparency: %w
AI-assisted analysis of grafana/k6@93accf6570 (2026-08-15).
Data as JSON: /api/errors/336c7fb92651db4f.
Report an issue: GitHub.