{"record":{"id":"336c7fb92651db4f","repo":"grafana/k6","slug":"persisting-screenshot-w","errorCode":null,"errorMessage":"persisting screenshot: %w","messagePattern":"persisting screenshot: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/js/modules/k6/browser/common/screenshotter.go","lineNumber":249,"sourceCode":"\t\tY:      doc.Y,\n\t\tWidth:  doc.Width,\n\t\tHeight: doc.Height,\n\t\tScale:  scale,\n\t}\n\tif clip.Width > 0 && clip.Height > 0 {\n\t\tcapture = capture.WithClip(clip)\n\t}\n\n\t// Capture screenshot\n\tbuf, err = capture.Do(cdp.WithExecutor(s.ctx, sess))\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"capturing screenshot: %w\", err)\n\t}\n\n\t// Save screenshot capture to file\n\tif path != \"\" {\n\t\tif err := s.persister.Persist(s.ctx, path, bytes.NewBuffer(buf)); err != nil {\n\t\t\treturn nil, fmt.Errorf(\"persisting screenshot: %w\", err)\n\t\t}\n\t}\n\n\treturn buf, nil\n}\n\nfunc getViewPortDimensions(ctx context.Context, sess session, logger *log.Logger) (float64, float64, float64, error) {\n\tvisualViewportScale := 1.0\n\tvisualViewportPageX, visualViewportPageY := 0.0, 0.0\n\n\t// Add clip region\n\t//nolint:dogsled\n\t_, _, _, _, cssVisualViewport, _, err := cdppage.GetLayoutMetrics().Do(cdp.WithExecutor(ctx, sess))\n\tif err != nil {\n\t\treturn 0, 0, 0, fmt.Errorf(\"getting layout metrics for screenshot: %w\", err)\n\t}\n\n\t// we had a null pointer panic cases, when visualViewport is nil","sourceCodeStart":231,"sourceCodeEnd":267,"githubUrl":"https://github.com/grafana/k6/blob/3fcf5388d78cb382f0c0d42ec556a06bfd1b8dee/internal/js/modules/k6/browser/common/screenshotter.go#L231-L267","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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'."],"exampleFix":"# before\nk6 run script.js  # script writes /var/out/shot.png (no dir, no perms)\n\n# after\nmkdir -p ./shots && chmod u+w ./shots\n# in script: page.screenshot({ path: './shots/home.png' })","handlingStrategy":"validation","validationCode":"import { mkdirSync, accessSync, constants } from 'fs';\nmkdirSync(dir, { recursive: true });\naccessSync(dir, constants.W_OK); // throws early if not writable\nawait page.screenshot({ path: `${dir}/shot.png` });","typeGuard":null,"tryCatchPattern":null,"preventionTips":["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."],"tags":["k6","browser","screenshot","filesystem","permissions"],"backgroundTag":"file-write-permission","analyzedSha":"3fcf5388d78cb382f0c0d42ec556a06bfd1b8dee","analyzedAt":"2026-08-18T03:05:52.393Z","contentChangedAt":"2026-08-18T03:05:52.393Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}