grafana/k6 · error
persisting screenshot
Error message
persisting screenshot: %w
What it means
Thrown when the screenshot was captured successfully but writing it to disk via the ScreenshotPersister failed. The persister writes bytes to the path option, so this is an ordinary file-system error: unwritable directory, permission denied, disk full, or a path that names a directory.
Solutions
- Create the target directory and verify write permission before the run: mkdir -p shots && touch shots/.probe.
- Use a relative path under the current working directory, which k6 can write to.
- Check the wrapped error — it is the OS error (permission denied, no such file or directory, no space left on device).
- Free disk space or pick another volume if 'no space left on device'.
Example fix
# before
k6 run script.js # script writes /var/out/shot.png (no dir, no perms)
# after
mkdir -p ./shots && chmod u+w ./shots
# in script: page.screenshot({ path: './shots/home.png' }) Defensive patterns
Strategy: validation
Validate before calling
import { mkdirSync, accessSync, constants } from 'fs';
mkdirSync(dir, { recursive: true });
accessSync(dir, constants.W_OK); // throws early if not writable
await page.screenshot({ path: `${dir}/shot.png` }); Prevention
- Pre-create screenshot directories in CI setup steps (mkdir -p).
- Use paths under the k6 working directory.
- Verify disk space in long screenshot-heavy runs.
When it happens
Trigger: page.screenshot({ path: '/root/shot.png' }) as a non-root user; path pointing into a nonexistent directory; read-only filesystem in a container; disk exhausted; path names an existing directory.
Common situations: CI containers running as an unprivileged user writing to paths like /app/out that were never created; k6 cloud/agent environments where the local disk is read-only; missing mkdir -p in pipelines.
Related errors
- capturing screenshot
- converting result ( of type %t) to size
- getting layout metrics for screenshot
- getting original viewport size
- getting viewport dimensions
AI-assisted analysis of grafana/k6@3fcf5388d7 (2026-08-18).
Data as JSON: /api/errors/336c7fb92651db4f.
Report an issue: GitHub.
Appendix: source
Thrown at internal/js/modules/k6/browser/common/screenshotter.go:249
Y: doc.Y,
Width: doc.Width,
Height: doc.Height,
Scale: scale,
}
if clip.Width > 0 && clip.Height > 0 {
capture = capture.WithClip(clip)
}
// Capture screenshot
buf, err = capture.Do(cdp.WithExecutor(s.ctx, sess))
if err != nil {
return nil, fmt.Errorf("capturing screenshot: %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 3fcf5388d7)