ThreeMammals/Ocelot · error · Exception

dotnet test failed with exit code

Error message

dotnet test failed with exit code {exitCode}

What it means

Thrown after 'dotnet test' (via coverlet collector) returns a non-zero exit code that is not the whitelisted code 7. Code 7 is deliberately treated as 'tests passed but background threads didn't exit cleanly' and only warns. Any other non-zero code means real test failures or a host crash.

Solutions

  1. Re-run the tests locally with the same arguments and inspect the failing test output.
  2. Fix the failing test or the regression it exposes (exit code 1 means test failure).
  3. Check trx/console logs for 'Test host process crashed' — fix SDK/runtime version mismatch if so.
  4. If the crash is thread-exit related but surfaces as a different code, add that code to the tolerated set only after verifying tests actually passed.

Example fix

// before
if (exitCode != 0 && exitCode != 7)
// after (only if verified non-failing)
if (exitCode != 0 && exitCode != 7 && exitCode != 8)
Defensive patterns

Strategy: try-catch

Validate before calling

// verify locally before CI
dotnet test ./test/Ocelot.UnitTests --collect:"XPlat Code Coverage"; echo $LASTEXITCODE

Try / catch

try { RunUnitTests(); }
catch (Exception ex) when (ex.Message.Contains("exit code")) { Warning($"Test runner failed: {ex.Message}"); throw; }

Prevention

When it happens

Trigger: dotnet test exiting with codes other than 0 or 7: at least one test failed (exit 1), test host crashed, unhandled exception in a test, or collection abort.

Common situations: A flaky or genuinely failing unit test after a code change; test host process crash on .NET SDK mismatch; deadlock in tests causing abort.


AI-assisted analysis of ThreeMammals/Ocelot@d1f22d9304 (2026-09-12). Data as JSON: /api/errors/d621ba4f3c3e2c28. Report an issue: GitHub.

Appendix: source

Thrown at build.cake:583

			*/
			// Use StartProcess instead of DotNetTest for better control
			var exitCode = StartProcess("dotnet", new ProcessSettings
			{
				Arguments = $"test \"{unitTestAssemblies}\" " +
							$"--configuration {compileConfig} " +
							$"--framework {tfm} " +
							$"--no-restore --no-build " +
							$"--verbosity {verbosity} " +
							$"--results-directory \"{artifactsForUnitTestsDir}\" " +
							$"--coverlet " +
							$"--coverlet-include \"[Ocelot*]*\" " +
							$"--coverlet-exclude \"[Ocelot.Testing]*\"",
				WorkingDirectory = "."
			});
			// Only fail on actual test failures, not on thread exit issues
			if (exitCode != 0 && exitCode != 7)
			{
				throw new Exception($"dotnet test failed with exit code {exitCode}");
			}
			else if (exitCode == 7)
			{
				Warning("Tests passed but background threads didn't exit cleanly (exit code 7). Ignoring.");
			}
		}
		
		Information("ArtifactsForUnitTestsDir = " + artifactsForUnitTestsDir);
		// Find all files matching pattern "coverage.cobertura.*.xml"
		var coverageFiles = GetFiles(artifactsForUnitTestsDir.ToString() + "/coverage.cobertura.*.xml");
		if (!coverageFiles.Any())
			throw new Exception($"No coverage.cobertura.*.xml files found in {artifactsForUnitTestsDir}");
		// Get the first matching file (or order by creation date if needed)
		var coverageSummaryFile = coverageFiles.First();
		Information("CoverageSummaryFile = " + coverageSummaryFile);
		GenerateReport(coverageSummaryFile);
		Information("##############################");
		Information("# Code coverage");

View on GitHub (pinned to d1f22d9304)