{"record":{"id":"7affd5fafd23ce93","repo":"stride3d/stride","slug":"rundirectory-has-no-store-base-directory","errorCode":null,"errorMessage":"'{runDirectory}' has no store base directory.","messagePattern":"'(.+?)' has no store base directory\\.","errorType":"exception","errorClass":"ArgumentException","httpStatus":null,"severity":"error","filePath":"sources/crashreport/Stride.CrashReporter/CrashSession.cs","lineNumber":54,"sourceCode":"\n    /// <summary>The deduped crash groups in this run that are not already suppressed.</summary>\n    public IReadOnlyList<StoredCrash> Groups { get; }\n\n    /// <summary>The run directory on disk (for a \"reveal in file manager\" that also keeps the files).</summary>\n    public string RunDirectory => run.Directory;\n\n    /// <summary>Crash sending was turned off at build time (StrideSentryDsn=false); offer no Send.</summary>\n    public bool IsDisabled => CrashReportSender.IsDisabled;\n\n    /// <summary>\n    /// Loads a run directory. The store layout is <c>&lt;base&gt;/&lt;app&gt;/run-*</c>, so the app id and base\n    /// are the run's parent and grandparent — enough to also reach the app's suppression list.\n    /// </summary>\n    public static CrashSession Load(string runDirectory, string? dsnOverride, bool sessionScoped, int? hostProcessId = null, IntPtr ownerWindow = default)\n    {\n        var full = Path.GetFullPath(runDirectory);\n        var appDir = Directory.GetParent(full) ?? throw new ArgumentException($\"'{runDirectory}' has no parent app directory.\");\n        var baseDir = appDir.Parent ?? throw new ArgumentException($\"'{runDirectory}' has no store base directory.\");\n\n        var store = new CrashStore(appDir.Name, baseDir.FullName);\n        var run = CrashStore.OpenRun(full);\n        var dsn = CrashReportSender.ResolveDsn(dsnOverride);\n\n        // Defensive: capture already skips suppressed signatures, but never re-surface one that slipped through.\n        var groups = run.Read().Where(crash => !store.IsSuppressed(crash.Signature, crash.Version)).ToList();\n        return new CrashSession(store, run, dsn, sessionScoped, hostProcessId, ownerWindow, groups);\n    }\n\n    /// <summary>True when a full-memory dump of the crashed host can be written on demand: the host itself crashed\n    /// (<c>--host-pid</c>) and is still alive, blocked until this window closes. Windows only (dbghelp).</summary>\n    public bool CanDumpHost => hostProcessId is not null && OperatingSystem.IsWindows();\n\n    /// <summary>\n    /// Writes a full-memory dump of the waiting host to <paramref name=\"path\"/>, with the report text beside it (a\n    /// dump only makes sense with its context). Strictly on demand: it can be several GB and is unscrubbed, so it\n    /// is never written unasked and never sent. False when there is no live host or the dump failed.","sourceCodeStart":36,"sourceCodeEnd":72,"githubUrl":"https://github.com/stride3d/stride/blob/96fad776d210c221682aac1ccdf4c79dc046fc38/sources/crashreport/Stride.CrashReporter/CrashSession.cs#L36-L72","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"// before\nvar dir = Path.Combine(\"C:\\\\\", \"run-1\"); // grandparent missing\nvar session = CrashSession.Load(dir, dsn, false);\n// after\nvar dir = Path.Combine(storeBase, \"MyApp\", \"run-1\");\nvar session = CrashSession.Load(dir, dsn, false);","handlingStrategy":"validation","validationCode":"var appDir = Directory.GetParent(Path.GetFullPath(runDirectory));\nif (appDir?.Parent is null)\n    throw new ArgumentException(\"Run directory must be nested as <base>/<app>/run-*; base directory missing\");","typeGuard":"bool HasStoreLayout(string runDir) {\n    var app = Directory.GetParent(Path.GetFullPath(runDir));\n    return app?.Parent != null;\n}","tryCatchPattern":"try { var session = CrashSession.Load(runDir, dsn, false); }\ncatch (ArgumentException e) when (e.Message.Contains(\"no store base directory\"))\n{ /* move/relocate the store off the root */ }","preventionTips":["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"],"tags":["crash-reporting","filesystem","path"],"backgroundTag":"directory-not-found","analyzedSha":"96fad776d210c221682aac1ccdf4c79dc046fc38","analyzedAt":"2026-09-14T02:59:31.279Z","contentChangedAt":"2026-09-14T02:59:31.279Z","schemaVersion":2},"datasetVersion":"2026-09-15T23:17:13.987Z"}