grafana/k6 · critical
ErrFatal
ErrFatal
Error message
fatal error
What it means
k6error.ErrFatal (k6error/internal.go:8-15) is a sentinel, not a standalone message: browser-module errors wrapped with it — e.g. 'on create page event failed to return a page' (browser_context.go:421) — tell the mapping layer, via panicIfFatalError (browser/helpers.go:20), to abort the whole test run instead of just the current iteration, because the error would recur with certainty on retry.
Source
Thrown at internal/js/modules/k6/browser/k6error/internal.go:15
// Package k6error contains ErrFatal.
package k6error
import (
"errors"
)
// ErrFatal should be wrapped into an error
// to signal to the mapping layer that the error
// is a fatal error and we should abort the whole
// test run, not just the current iteration. It
// should be used in cases where if the iteration
// ran again then there's a 100% chance that it
// will end up running into the same error.
var ErrFatal = errors.New("fatal error")
View on GitHub (pinned to 93accf6570)
Solutions
- Read the wrapped cause printed alongside 'fatal error' — it names the real failure; fix that root cause
- Fix lifecycle handling: don't close the browser context while page creation or events are pending
- Create pages fresh per iteration from a long-lived context instead of reusing closed handles
- Upgrade k6 to the latest version if the wrapped cause points at an internal browser-module bug
Example fix
// before
const page = await context.newPage();
// ... later, in another iteration reusing a closed context:
const page2 = await context.newPage(); // fatal after close
// after
// keep the context open for the whole test; create pages per iteration
export default async function () {
const page = await context.newPage();
await page.goto('https://example.com/');
await page.close();
} Defensive patterns
Strategy: try-catch
Validate before calling
// Structural preflight: keep the context alive for the whole test
if (!context || context.isClosed?.()) { context = await browser.newContext(); } // keep open across iterations
const page = await context.newPage(); Try / catch
try {
const page = await context.newPage();
} catch (e) {
if (String(e.message).includes('fatal error')) {
// abort the run deliberately: report and exit non-zero, do not retry
console.error('fatal browser error, aborting:', e.message);
throw e;
}
throw e;
} Prevention
- Never close the browser context while page creation or events are pending
- Create pages per iteration from a long-lived context; don't reuse closed handles
- Read the wrapped cause next to 'fatal error' — fix the root condition, retrying cannot help
- Stay on recent k6 releases for browser-module lifecycle fixes
When it happens
Trigger: Structural failures during browser lifecycle operations: page-creation events yielding a non-page object, pages/contexts used after close, or any internal state where re-running the iteration cannot succeed.
Common situations: Racing context.close() with pending page creation; reusing pages or contexts across iterations after they were closed; internal k6/browser bugs in specific versions where an event returns an unexpected object.
Related errors
- iteration ended before page.on handler completed executing
- cannot close context as none is active in browser
- attaching new page: %w
- browser.on promise rejected: %w
- could not find frame with id %s
AI-assisted analysis of grafana/k6@93accf6570 (2026-08-15).
Data as JSON: /api/errors/e8b8d4e489c4db79.
Report an issue: GitHub.