stride3d/stride · error · ArgumentException

' ' has no parent app directory.

Error message

'{runDirectory}' has no parent app directory.

What it means

CrashSession.Load expects a run directory laid out as <base>/<app>/run-*. The run directory's parent must be the app directory; if the path has no parent (e.g. a filesystem root), the app id cannot be determined and the load aborts with this ArgumentException.

Solutions

  1. Pass the actual run directory (e.g. <base>/<app>/run-2024-01-01) to Load
  2. Guard the input: ensure Path.GetFullPath(runDirectory) has a parent before calling Load
  3. Fix any path-building code that points at the root or the base directory

Example fix

// before
var session = CrashSession.Load(storeBaseDir, dsn, false); // base dir, wrong level
// after
var runDir = Directory.GetDirectories(storeBaseDir, "*", SearchOption.AllDirectories)
    .First(d => Path.GetFileName(d).StartsWith("run-"));
var session = CrashSession.Load(runDir, dsn, false);
Defensive patterns

Strategy: validation

Validate before calling

var full = Path.GetFullPath(runDirectory);
if (Directory.GetParent(full) is null)
    throw new ArgumentException($"'{runDirectory}' is not a valid run directory (no parent app directory)");

Type guard

bool IsLikelyRunDirectory(string path) =>
    Path.GetFileName(path).StartsWith("run-") && Directory.GetParent(Path.GetFullPath(path)) != null;

Try / catch

try { var session = CrashSession.Load(runDir, dsn, false); }
catch (ArgumentException e) when (e.Message.Contains("no parent app directory"))
{ /* correct the path or re-locate the run directory */ }

Prevention

When it happens

Trigger: Calling CrashSession.Load on a path that is a root directory or otherwise has no parent (Directory.GetParent returns null), instead of a run-* folder nested under an app directory.

Common situations: Passing the wrong level of the store hierarchy (the base or app directory, or "/") instead of a run directory; programmatic path building that concatenates incorrectly.

Related errors


AI-assisted analysis of stride3d/stride@96fad776d2 (2026-09-14). Data as JSON: /api/errors/a9d54032e9a0bff7. Report an issue: GitHub.

Appendix: source

Thrown at sources/crashreport/Stride.CrashReporter/CrashSession.cs:53

    public IntPtr OwnerWindow { get; }

    /// <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>&lt;base&gt;/&lt;app&gt;/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

View on GitHub (pinned to 96fad776d2)