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 for the duration of a local run. On the first signal it gracefully stops the test, propagates this error (exit code 105, ExternalAbort, abort reason AbortedByUser), and cancels lingering contexts. A second signal triggers the hard-stop path instead. The signal name is included in the message.
Solutions
- If you stopped the test intentionally, no fix is needed — this is the expected outcome
- In CI, raise the job/step timeout so k6 finishes before the runner sends SIGTERM
- For containers/orchestrators, allow a generous graceful termination period before SIGKILL so k6 can flush metrics and the summary
- Treat exit code 105 as 'cancelled/aborted by user', not as a test failure, in pipeline result mapping
Defensive patterns
Strategy: try-catch
Try / catch
# distinguish user/system aborts (105) from real failures k6 run script.js ec=$? if [ "$ec" -eq 105 ]; then echo "test aborted by SIGINT/SIGTERM — marking job as cancelled" >&2 exit 0 # or a dedicated 'cancelled' status fi exit "$ec"
Prevention
- Size CI timeouts larger than the longest planned test duration plus teardown
- Set graceful termination periods (terminationGracePeriodSeconds, docker stop -t) above k6's teardown needs
- Avoid sending a second signal immediately — the first already triggers graceful stop
- Record signal-based aborts separately in dashboards so they don't pollute failure metrics
When it happens
Trigger: Ctrl+C in a terminal; CI/container runtimes sending SIGTERM on job timeout or pod eviction; `timeout 60 k6 run ...`; systemd stop; orchestrators signaling the whole process group.
Common situations: Kubernetes pod eviction mid-test; CI step time limits smaller than the scripted duration; shared agents reaping long jobs; accidental double Ctrl+C.
Related errors
AI-assisted analysis of grafana/k6@3fcf5388d7 (2026-08-18).
Data as JSON: /api/errors/daa6c7a0a67caed1.
Report an issue: GitHub.
Appendix: source
Thrown at internal/cmd/run.go:409
defer func() {
logger.Debug("Waiting for metrics processing to finish...")
close(samples)
waitOutputsFlushed()
logger.Debug("Metrics 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 3fcf5388d7)