grafana/k6 · warning

105

105

Error message

test run was aborted because k6 received a '%s' signal

What it means

k6 traps SIGINT and SIGTERM during a local run. On the first signal the gracefulStop handler aborts the test with this error and exit code 105 (ExternalAbort), abort reason AbortedByUser, so the reason for the incomplete run propagates instead of a silent exit. A second signal triggers the hard stop. This is intentional documented behavior, not a crash.

Source

Thrown at internal/cmd/run.go:411

			}()
		}
		wg.Wait()

		logger.Debug("Metrics and traces processing finished!")
	}()

	printExecutionDescription(
		c.gs, "local", args[0], "", conf, executionState.ExecutionTuple, executionPlan, outputs,
	)

	// Trap Interrupts, SIGINTs and SIGTERMs.
	// TODO: move upwards, right after runCtx is created
	gracefulStop := func(sig os.Signal) {
		logger.WithField("sig", sig).Debug("Stopping k6 in response to signal...")
		// first abort the test run this way, to propagate the error
		runAbort(errext.WithAbortReasonIfNone(
			errext.WithExitCodeIfNone(
				fmt.Errorf("test run was aborted because k6 received a '%s' signal", sig), exitcodes.ExternalAbort,
			), errext.AbortedByUser,
		))
		lingerCancel() // cancel this context as well, since the user did Ctrl+C
	}
	onHardStop := func(sig os.Signal) {
		logger.WithField("sig", sig).Error("Aborting k6 in response to signal")
		globalCancel() // not that it matters, given that os.Exit() will be called right after
	}
	stopSignalHandling := handleTestAbortSignals(c.gs, gracefulStop, onHardStop)
	defer stopSignalHandling()

	// Initialize the VUs and executors
	stopVUEmission, err := execScheduler.Init(runCtx, samples)
	if err != nil {
		return err
	}
	defer stopVUEmission()

View on GitHub (pinned to 93accf6570)

Solutions

  1. Treat exit code 105 as 'canceled by user/system' rather than a product failure in CI and wrapper scripts
  2. For intentional stops, prefer the REST API ('k6 stop' or POST /v1/status) which performs a graceful stop
  3. If the signal is unintentional, raise the CI timeout or container stop grace period so the test can finish
  4. For headless execution, start k6 detached (nohup, systemd, background runner) so terminal signals don't reach it

Example fix

# before
k6 run script.js || exit 1   # Ctrl+C marks the CI job failed
# after
k6 run script.js
rc=$?
if [ $rc -eq 105 ]; then echo 'canceled by signal'; exit 0; fi
exit $rc
Defensive patterns

Strategy: fallback

Try / catch

#!/usr/bin/env bash
k6 run script.js &
pid=$!
trap '' INT   # let k6 own the first Ctrl+C
wait $pid
case $? in
  105) echo 'canceled by signal — not a failure'; exit 0;;
  *)   exit $?;;
esac

Prevention

When it happens

Trigger: Pressing Ctrl+C while 'k6 run' is executing; a process manager (docker stop, systemd, Kubernetes pod eviction, CI timeout) sending SIGTERM; handleTestAbortSignals routes the first signal to gracefulStop which calls runAbort with the wrapped error.

Common situations: CI jobs killed on timeout report exit 105; containers stopped within the default grace period; long soak tests interrupted manually; wrapper scripts not distinguishing 105 from genuine failures.

Related errors


AI-assisted analysis of grafana/k6@93accf6570 (2026-08-15). Data as JSON: /api/errors/daa6c7a0a67caed1. Report an issue: GitHub.