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
- Treat exit code 105 as 'canceled by user/system' rather than a product failure in CI and wrapper scripts
- For intentional stops, prefer the REST API ('k6 stop' or POST /v1/status) which performs a graceful stop
- If the signal is unintentional, raise the CI timeout or container stop grace period so the test can finish
- 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
- Map exit code 105 to 'canceled' in CI wrappers instead of failure
- Use 'k6 stop' or the REST API for intentional stops
- Size CI timeouts to the full test duration so k6 is never SIGTERMed mid-run
- Send a second signal only when a hard stop is truly wanted
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
- k6 couldn't detect google chrome or a chromium-supported bro
- browser process ended unexpectedly
- {msg}
- 103
- test run aborted before starting: %s
AI-assisted analysis of grafana/k6@93accf6570 (2026-08-15).
Data as JSON: /api/errors/daa6c7a0a67caed1.
Report an issue: GitHub.