stride3d/stride · error · ArgumentException
' ' has no store base directory.
Error message
'{runDirectory}' has no store base directory. What it means
Beyond requiring a parent (app) directory, CrashSession.Load also requires a grandparent — the store base directory — so it can locate the app's suppression list and other store data. If the app directory itself has no parent (i.e. it is a root's child), loading aborts with this ArgumentException.
Solutions
- Place the crash store at least three levels deep: <base>/<app>/run-*, e.g. C:\Users\me\AppData\Crashes\MyApp\run-1
- Validate the layout (run dir has both parent and grandparent) before calling Load
- Move the store off the filesystem root if it was deployed there
Example fix
// before
var dir = Path.Combine("C:\\", "run-1"); // grandparent missing
var session = CrashSession.Load(dir, dsn, false);
// after
var dir = Path.Combine(storeBase, "MyApp", "run-1");
var session = CrashSession.Load(dir, dsn, false); Defensive patterns
Strategy: validation
Validate before calling
var appDir = Directory.GetParent(Path.GetFullPath(runDirectory));
if (appDir?.Parent is null)
throw new ArgumentException("Run directory must be nested as <base>/<app>/run-*; base directory missing"); Type guard
bool HasStoreLayout(string runDir) {
var app = Directory.GetParent(Path.GetFullPath(runDir));
return app?.Parent != null;
} Try / catch
try { var session = CrashSession.Load(runDir, dsn, false); }
catch (ArgumentException e) when (e.Message.Contains("no store base directory"))
{ /* move/relocate the store off the root */ } Prevention
- Create the crash store at least three levels deep, never on a filesystem root
- Use a conventional base such as %LOCALAPPDATA% or XDG data dir
- Validate the grandparent exists before loading a run directory
When it happens
Trigger: Calling CrashSession.Load on a run directory directly under a filesystem root, e.g. C:\run-x or /run-x, where appDir.Parent is null.
Common situations: Extracting or creating the crash store at a drive/mount root during testing; using temp paths like /tmp-as-root or drive roots instead of a nested store layout.
Related errors
- ' ' has no parent app directory.
- Base path must be non-empty (use null for passthrough).
- assetItem must be an absolute path
- The relativePath argument is null or empty
- Expecting relative path
AI-assisted analysis of stride3d/stride@96fad776d2 (2026-09-14).
Data as JSON: /api/errors/7affd5fafd23ce93.
Report an issue: GitHub.
Appendix: source
Thrown at sources/crashreport/Stride.CrashReporter/CrashSession.cs:54
/// <summary>The deduped crash groups in this run that are not already suppressed.</summary>
public IReadOnlyList<StoredCrash> Groups { get; }
/// <summary>The run directory on disk (for a "reveal in file manager" that also keeps the files).</summary>
public string RunDirectory => run.Directory;
/// <summary>Crash sending was turned off at build time (StrideSentryDsn=false); offer no Send.</summary>
public bool IsDisabled => CrashReportSender.IsDisabled;
/// <summary>
/// Loads a run directory. The store layout is <c><base>/<app>/run-*</c>, so the app id and base
/// are the run's parent and grandparent — enough to also reach the app's suppression list.
/// </summary>
public static CrashSession Load(string runDirectory, string? dsnOverride, bool sessionScoped, int? hostProcessId = null, IntPtr ownerWindow = default)
{
var full = Path.GetFullPath(runDirectory);
var appDir = Directory.GetParent(full) ?? throw new ArgumentException($"'{runDirectory}' has no parent app directory.");
var baseDir = appDir.Parent ?? throw new ArgumentException($"'{runDirectory}' has no store base directory.");
var store = new CrashStore(appDir.Name, baseDir.FullName);
var run = CrashStore.OpenRun(full);
var dsn = CrashReportSender.ResolveDsn(dsnOverride);
// Defensive: capture already skips suppressed signatures, but never re-surface one that slipped through.
var groups = run.Read().Where(crash => !store.IsSuppressed(crash.Signature, crash.Version)).ToList();
return new CrashSession(store, run, dsn, sessionScoped, hostProcessId, ownerWindow, groups);
}
/// <summary>True when a full-memory dump of the crashed host can be written on demand: the host itself crashed
/// (<c>--host-pid</c>) and is still alive, blocked until this window closes. Windows only (dbghelp).</summary>
public bool CanDumpHost => hostProcessId is not null && OperatingSystem.IsWindows();
/// <summary>
/// Writes a full-memory dump of the waiting host to <paramref name="path"/>, with the report text beside it (a
/// dump only makes sense with its context). Strictly on demand: it can be several GB and is unscrubbed, so it
/// is never written unasked and never sent. False when there is no live host or the dump failed.View on GitHub (pinned to 96fad776d2)