JasperFxOptions.DetermineCallingAssembly() can resolve the application assembly to the test runner (xunit.v3.core) rather than the test assembly. Every consumer that uses JasperFxOptions.ApplicationAssembly for type discovery then scans an assembly containing none of the suite's types.
This was the root cause of JasperFx/wolverine#3776 (fix: JasperFx/wolverine#3777). Wolverine has been defended at its own boundary, but the defect is here and Marten / Polecat hosts have the same exposure.
The code
JasperFxOptions.cs:243:
if (assemblyName.StartsWith("System") || assemblyName.StartsWith("Microsoft") || assemblyName.StartsWith("ReSharperTestRunner"))
{
continue;
}
return assembly;
The walk takes the first frame outside JasperFx that isn't System*/Microsoft*/ReSharperTestRunner*. Under an async test fixture the frames between JasperFx and the test class belong to the runner, not the test assembly — so xunit.v3.core satisfies the filter and is returned. The ReSharperTestRunner entry shows this class of problem was already known; xUnit was simply never added.
The short-circuit above it does not save you: HasReferenceToJasperFxTool(Assembly.GetEntryAssembly()) returns False for an ordinary test project (no referenced assembly carries [JasperFxTool]), so the stack walk always runs. I verified this directly — False at both versions tested, with zero reference-load failures.
Measured impact
Instrumented CI runs of one Wolverine test shard, logging the adopted assembly per host, at a single fixed commit:
|
adopted assembly |
outcome |
| before fix |
xunit.v3.core on 79 of 82 hosts, 0–15 handler chains |
43 of 86 tests failed |
| after fix |
PolecatTests on 82 of 82, 141 chains |
86 passed, 0 retries |
The symptom is entirely downstream and gives no hint of the cause — in Wolverine's case IndeterminateRoutesException: Could not determine any valid subscribers or local handlers. For Marten the equivalent would be a document/projection type simply not being discovered.
It is also unstable rather than consistently wrong: the same shard resolved correctly on 82/82 hosts on one build and incorrectly on 79/82 on another, from source that differed only by a version string, on the same runner image minutes apart. A stack walk over async continuations is sensitive to frame layout, so this presents as an intermittent, environment-specific failure.
Suggested fix
Extend the skip list to test-runner assemblies. This is what Wolverine now does locally (WolverineOptions.IsTestRunnerAssembly):
assemblyName.StartsWith("xunit", StringComparison.OrdinalIgnoreCase)
|| assemblyName.StartsWith("nunit", StringComparison.OrdinalIgnoreCase)
|| assemblyName.StartsWith("TUnit", StringComparison.OrdinalIgnoreCase)
|| assemblyName.StartsWith("MSTest", StringComparison.OrdinalIgnoreCase)
|| assemblyName.StartsWith("testhost", StringComparison.OrdinalIgnoreCase)
|| assemblyName.StartsWith("ReSharperTestRunner", StringComparison.OrdinalIgnoreCase)
|| assemblyName.StartsWith("JetBrains.", StringComparison.OrdinalIgnoreCase)
Skipping the runner lets the walk continue out to the test assembly, and where nothing else matches the existing Assembly.GetEntryAssembly() fallback is the test assembly anyway.
Worth pairing with a regression test that asserts a host bootstrapped from a test assembly never adopts a runner assembly — that is the check that would have caught this, and it is cheap.
🤖 Generated with Claude Code
https://claude.ai/code/session_013eR4GL278688VhyhrGcttJ
JasperFxOptions.DetermineCallingAssembly()can resolve the application assembly to the test runner (xunit.v3.core) rather than the test assembly. Every consumer that usesJasperFxOptions.ApplicationAssemblyfor type discovery then scans an assembly containing none of the suite's types.This was the root cause of JasperFx/wolverine#3776 (fix: JasperFx/wolverine#3777). Wolverine has been defended at its own boundary, but the defect is here and Marten / Polecat hosts have the same exposure.
The code
JasperFxOptions.cs:243:The walk takes the first frame outside JasperFx that isn't
System*/Microsoft*/ReSharperTestRunner*. Under an async test fixture the frames between JasperFx and the test class belong to the runner, not the test assembly — soxunit.v3.coresatisfies the filter and is returned. TheReSharperTestRunnerentry shows this class of problem was already known; xUnit was simply never added.The short-circuit above it does not save you:
HasReferenceToJasperFxTool(Assembly.GetEntryAssembly())returnsFalsefor an ordinary test project (no referenced assembly carries[JasperFxTool]), so the stack walk always runs. I verified this directly —Falseat both versions tested, with zero reference-load failures.Measured impact
Instrumented CI runs of one Wolverine test shard, logging the adopted assembly per host, at a single fixed commit:
xunit.v3.coreon 79 of 82 hosts, 0–15 handler chainsPolecatTestson 82 of 82, 141 chainsThe symptom is entirely downstream and gives no hint of the cause — in Wolverine's case
IndeterminateRoutesException: Could not determine any valid subscribers or local handlers. For Marten the equivalent would be a document/projection type simply not being discovered.It is also unstable rather than consistently wrong: the same shard resolved correctly on 82/82 hosts on one build and incorrectly on 79/82 on another, from source that differed only by a version string, on the same runner image minutes apart. A stack walk over async continuations is sensitive to frame layout, so this presents as an intermittent, environment-specific failure.
Suggested fix
Extend the skip list to test-runner assemblies. This is what Wolverine now does locally (
WolverineOptions.IsTestRunnerAssembly):Skipping the runner lets the walk continue out to the test assembly, and where nothing else matches the existing
Assembly.GetEntryAssembly()fallback is the test assembly anyway.Worth pairing with a regression test that asserts a host bootstrapped from a test assembly never adopts a runner assembly — that is the check that would have caught this, and it is cheap.
🤖 Generated with Claude Code
https://claude.ai/code/session_013eR4GL278688VhyhrGcttJ