-
Notifications
You must be signed in to change notification settings - Fork 1
Troubleshooting
Thrown when Atlas cannot prepare the test environment. Common causes:
-
VINTAGE_STORYis unset, or does not point at a folder containingVintagestoryAPI.dll. Atlas needs this to locate the game's binaries and libraries for assembly resolution. -
VintagestoryAPI.dllis present in the test output withoutVintagestoryAPI.pdb. The boot preflight fails fast because the game cannot initialize its logger without the pdb; see the dedicated section below for the root cause and fixes. -
A mod path in
[assembly: AtlasMods(...)](or[AtlasWorld(Mods = ...)]) does not resolve to an existing folder,.zip, or.dllrelative to the test assembly's output directory.ModStager.Stagenames every missing path in the exception message. See Mod Staging for path resolution rules. -
Bridge staging copy failure.
ModStager.StageBridgewraps any file system failure (locked file, permissions, missing source) copyingAtlasBridge.dllinto the scratch mods folder, naming both the source and destination paths and carrying the underlying file system error as the inner exception. -
"Bridge mod did not start" or a mod loader rejection (bad
modinfo.json, dependency resolution failure): the exception message includes the mod loader's own report. Check the embedded server's own logs, which land in the scenario's scratch data path (printed on failure; look for a temp directory created per test class under the OS temp folder). -
Two hosts requested concurrently:
HostRegistry.GetOrCreateAsyncthrows this if a second scenario class tries to get a host while one is already in flight. This should only happen if[assembly: CollectionBehavior(DisableTestParallelization = true)]is missing; see the one-live-server section below. - "Test player 'X' did not finish joining the world": the server rejected the synthetic join. Most likely the player name breaks the engine's rule (letters, digits, underscores and dashes only, 16 characters at most - the number one cause in practice); otherwise a game network-version drift relative to the Atlas build. The server's own log (in the scratch data path named by the message) states the exact reason.
-
"A test player named 'X' already joined this class's world": the class host's world is
shared by every scenario in the class, and a player joined by an earlier scenario is still
connected. Reuse that
ITestPlayer(share it via a field), join under a different name (several players per world are supported), or isolate the scenario with[AtlasScenario(FreshWorld = true)].
Symptom (on Atlas versions without the preflight): every scenario fails during the first server boot with
TypeInitializationException: The type initializer for 'Vintagestory.API.Common.LoggerBase' threw an exception.
---> NullReferenceException
surfacing from ServerHost.ConfigureEngineStatics (the first ServerLogger construction).
Current Atlas versions detect the condition up front and throw an AtlasSetupException
naming the offending directory instead.
Root cause (verified by decompiling Vintage Story 1.22.0): LoggerBase's static
constructor deliberately throws a dummy exception and derives its SourcePath from
new StackTrace(e, fNeedFileInfo: true).GetFrame(0).GetFileName(). That file name only
exists when the pdb sits next to the loaded VintagestoryAPI.dll; without it,
GetFileName() returns null and the subsequent .Split(...) throws. A
VintagestoryAPI.dll copy in your test output wins default assembly probing over the game
install's copy, so if that copy shipped without VintagestoryAPI.pdb, the boot dies.
How you get there: a vendored dll (checked into the repo without its pdb), a custom copy
step, or a <Reference> whose HintPath points at a location holding only the dll. A plain
<Reference> with a HintPath into the game install is safe - MSBuild copies related files
(pdb, xml) along with the dll.
Fix, either:
- ship the matching
VintagestoryAPI.pdbnext to the dll (it must be the pdb produced with that exact dll - a pdb from a different game version has the wrong debug GUID and the crash comes back), or - stop copying the dll (
<Private>false</Private>on the reference) so probing falls through to Atlas's resolve hook, which loads the game install's copy with its pdb beside it.
Atlas.E2E.targets (shipped buildTransitive in the NuGet package) also emits a build
warning when it sees VintagestoryAPI.dll land in the output directory without its pdb.
If you installed Pixnop.Atlas.XUnit from NuGet, this almost always means VINTAGE_STORY was
unset at build time: the package's buildTransitive target needs it to find the game's own
Newtonsoft.Json.dll and copy it over the test SDK's transitive copy. For NuGet consumers
this target runs automatically, no <Import> needed; set VINTAGE_STORY and rebuild.
If you are building Atlas from source instead (a ProjectReference rather than a
PackageReference), the buildTransitive packaging step never runs, so you additionally need
<Import Project=".../build/Atlas.E2E.targets" /> in the test project. Verify
$(VintageStoryPath) resolves (it defaults from the VINTAGE_STORY environment variable),
add the import if missing, rebuild, and re-run. See Getting Started.
Both the build (compiling against VintagestoryAPI.dll) and the test run (booting the
embedded server) need this environment variable. Set it once in your shell profile or CI
environment to the folder containing VintagestoryAPI.dll:
export VINTAGE_STORY=/opt/vintagestory-
ScenarioTimeoutException: thrown either byWorld.Until(...)whentimeoutTickselapses (tick-based, carriesTicksWaited), or by the off-threadWatchdogwhenTimeoutMselapses (wall-clock, independent of the server's own ticking). In the watchdog case,HostRegistry.MarkDeadalso marks the class host dead, since the game thread may still be running the abandoned scenario. -
ServerCrashedException: thrown when the embedded server itself dies mid-scenario. The original crash is captured as the exception's inner exception and rethrown into the owning xUnit test; the class host is marked dead the same way, so remaining scenarios in the class fail fast with a clear message rather than cascading into opaque timeouts.
Vintage Story 1.22.2 has a known shutdown flake in the embedded server: it occasionally
throws a NullReferenceException from ServerSystemMonitor.Dispose() while tearing down.
Atlas catches and swallows this specific failure at teardown; it does not affect scenario
results and is not something a test author needs to work around. Tracked upstream as
issue #8 for visibility.
await World.Until(...) timeouts are tick-based, which only elapses while the server is
actually ticking. If the server itself is stuck, the scenario's TimeoutMs watchdog (default
60 seconds of wall-clock time, set via [AtlasScenario(TimeoutMs = ...)]) is what actually
fails the test. If a run hangs for substantially longer than that, check for a
ConfigureAwait(false) in the scenario body: it detaches the continuation from the game
thread's queue, which can produce hangs that the watchdog cannot observe correctly because
the continuation itself never reaches the awaited task in the expected way. See
Writing Scenarios for the full game-thread rule.
Atlas hosts at most one live server per process. If a second scenario class requests a host
while another is still in flight, HostRegistry.GetOrCreateAsync throws AtlasSetupException
with a message pointing at the missing
[assembly: CollectionBehavior(DisableTestParallelization = true)] declaration. This
attribute is required in every Atlas test assembly; without it, xUnit may attempt to run
scenario classes concurrently, which Atlas cannot support (see Architecture for why:
the embedded server relies on process-wide statics).