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

  1. Read the stack trace in the output — the top k6/extension frames name the faulting component
  2. Search or file an issue at github.com/grafana/k6/issues with the full stack trace, `k6 version` output, and a minimal reproducer script
  3. If an xk6 extension is involved, rebuild the binary so the extension matches your k6 version (xk6 build with matching --with pins)
  4. 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

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)