grafana/k6 · error

110

110

Error message

test run was marked as failed

What it means

execution.test.fail(msg) (k6/execution, also surfaced via the experimental test module) marks the whole test as failed while letting the run finish — unlike test.abort(), which interrupts it. After execution completes, run() checks TestStatus.Failed() and returns this error with exit code 110 (MarkedAsFailed). The reason string passed to fail() is also logged at ERROR level during the run, which is the fastest way to find the call site.

Solutions

  1. Search the script for 'test.fail' and read the matching ERROR log line — it contains the reason string you passed
  2. Fix the condition that triggered the failure (usually a real application error the script correctly detected)
  3. If the condition should not be fatal, downgrade to check() or console.error instead of test.fail()
  4. In CI, map exit code 110 to 'script-asserted failure', distinct from 99 (thresholds) and 105 (aborted)

Example fix

// before
import execution from 'k6/execution';
if (res.status >= 500) execution.test.fail('got 5xx');

// after (non-fatal assertion)
import { check } from 'k6';
check(res, { 'status is 2xx': (r) => r.status >= 200 && r.status < 300 });
Defensive patterns

Strategy: validation

Try / catch

# map exit 110 to a script-assertion failure bucket
k6 run script.js
ec=$?
case $ec in
  110) echo "script called execution.test.fail() — see ERROR lines in the log for the reason"; exit 110;;
  *)   exit $ec;;
esac

Prevention

When it happens

Trigger: Calling execution.test.fail('login endpoint returned 5xx') anywhere in VU code when an application-level condition is detected; the run then completes all iterations and exits 110 instead of 0.

Common situations: Business-assertion scripts that must fail the run on content errors (wrong JSON, missing field) that HTTP metrics and thresholds don't capture; smoke tests asserting critical flows.

Related errors


AI-assisted analysis of grafana/k6@94169daab2 (2026-08-18). Data as JSON: /api/errors/6464a16f36f79e82. Report an issue: GitHub.

Appendix: source

Thrown at internal/cmd/run.go:487

	// Check what the execScheduler.Run() error is.
	if err != nil {
		err = common.UnwrapSobekInterruptedError(err)
		logger.WithError(err).Debug("Test finished with an error")
		return err
	}

	// Warn if no iterations could be completed.
	if executionState.GetFullIterationCount() == 0 {
		logger.Warn("No script iterations fully finished, consider making the test duration longer")
	}

	// The execution module enables users to mark a test as failed, while letting the test
	// execution complete. As such, we check the test status here, after the test run has finished, and
	// ensure we return an error indicating that the test run was marked as failed, and the proper
	// exit code is used.
	if testRunState.TestStatus.Failed() {
		return errext.WithExitCodeIfNone(
			fmt.Errorf("test run was marked as failed"),
			exitcodes.MarkedAsFailed,
		)
	}

	logger.Debug("Test finished cleanly")

	return nil
}

func getSummaryMode(runtimeOptions lib.RuntimeOptions) (summary.Mode, bool, error) {
	sm, err := summary.ValidateMode(runtimeOptions.SummaryMode.String)
	if err != nil {
		return summary.ModeDisabled, false, err
	}

	return sm, sm != summary.ModeDisabled, nil
}

View on GitHub (pinned to 94169daab2)