grafana/k6 · critical
109
109
Error message
unexpected k6 panic: %s %s
What it means
This is k6's top-level recover() in rootCommand.execute(): an unrecovered Go panic occurred somewhere in the k6 process, and k6 caught it to print the panic value plus a full goroutine stack dump (debug.Stack()) before exiting with code 109 (exitcodes.GoPanic). The message is intentionally generic; the stack trace printed with it identifies the actual fault, and it is logged to both the normal and fallback loggers (including remote Loki logging if configured).
Solutions
- Read the stack trace in the output — the top k6/extension frames name the faulting component
- Search or file an issue at github.com/grafana/k6/issues with the full stack trace, `k6 version` output, and a minimal reproducer script
- If an xk6 extension is involved, rebuild the binary so the extension matches your k6 version (xk6 build with matching --with pins)
- Try the latest k6 release; if it is a regression, pin the last known-good older version
Defensive patterns
Strategy: try-catch
Try / catch
# capture the panic report (exit code 109) instead of losing the stack trace set +e k6 run script.js > k6.log 2>&1 ec=$? set -e if [ "$ec" -eq 109 ]; then echo "k6 panicked — filing report" >&2 grep -A40 'unexpected k6 panic' k6.log # extract panic value + stack for the issue exit 109 fi
Prevention
- Pin the k6 version in CI and rebuild xk6 extension binaries against the exact same version
- Keep stderr captured to a file in every CI run so stack traces survive
- Reproduce panics with --verbose and a minimal script before reporting; include `k6 version` output in issues
- For long-running jobs, wrap k6 in a supervisor that records the log on non-zero exit
When it happens
Trigger: Any panic escaping normal error handling during a command: a bug in k6 core, a panic in an xk6 extension module, a race in the browser/CDP module, or a nil dereference in module code reached from JavaScript. Reproduce by running the same script; the panic fires deterministically or under load.
Common situations: Rebuilding a binary with an xk6 extension built against a different k6 version; hitting a known panic fixed in a later k6 release; browser-module concurrency bugs; corrupted state after out-of-memory conditions.
Related errors
AI-assisted analysis of grafana/k6@8d06114777 (2026-08-18).
Data as JSON: /api/errors/9ae742c235af950d.
Report an issue: GitHub.
Appendix: source
Thrown at internal/cmd/root.go:193
return nil
}
func (c *rootCommand) execute() {
ctx, cancel := context.WithCancel(c.globalState.Ctx)
c.globalState.Ctx = ctx
exitCode := -1
defer func() {
cancel()
c.stopLoggers()
c.globalState.OSExit(exitCode)
}()
defer func() {
if r := recover(); r != nil {
exitCode = int(exitcodes.GoPanic)
err := fmt.Errorf("unexpected k6 panic: %s\n%s", r, debug.Stack())
if c.loggerIsRemote {
c.globalState.FallbackLogger.Error(err)
}
c.globalState.Logger.Error(err)
}
}()
// Completion requests for unregistered extensions must bypass Execute:
// cobra's __complete writes to stdout as a side-effect, and the
// provisioned binary's output would append to it, producing double output.
var err error
if ext, ok := detectExtensionCompletion(c.cmd, c.globalState); ok {
err = completeExtension(c.globalState, ext, newCacheProvisioner(c.globalState))
} else {
var executed *cobra.Command
executed, err = c.cmd.ExecuteC()
// Report subcommand usage after the command runs, regardless of its exit
// error, so a failed subcommand is still counted.View on GitHub (pinned to 8d06114777)