{"record":{"id":"67e86a2e850be644","repo":"babalae/better-genshin-impact","slug":"bettergi-restartfromprocessid-value","errorCode":null,"errorMessage":"等待旧 BetterGI 进程 {restartFromProcessId.Value} 退出超时。","messagePattern":"等待旧 BetterGI 进程 (.+?) 退出超时。","errorType":"exception","errorClass":"TimeoutException","httpStatus":null,"severity":"error","filePath":"BetterGenshinImpact/Service/Instance/InstanceBootstrap.cs","lineNumber":162,"sourceCode":"    public void Dispose()\n    {\n        Interlocked.Exchange(ref _firstServer, null)?.Dispose();\n        Interlocked.Exchange(ref _firstRootConnection, null)?.Client.Dispose();\n    }\n\n    private static void WaitForRestartSource(int? restartFromProcessId)\n    {\n        if (restartFromProcessId is null || restartFromProcessId == Environment.ProcessId)\n        {\n            return;\n        }\n\n        try\n        {\n            using var process = Process.GetProcessById(restartFromProcessId.Value);\n            if (!process.WaitForExit(milliseconds: 15_000))\n            {\n                throw new TimeoutException(\n                    $\"等待旧 BetterGI 进程 {restartFromProcessId.Value} 退出超时。\");\n            }\n        }\n        catch (ArgumentException)\n        {\n            // 旧进程已经退出。\n        }\n    }\n\n    private static async Task<InitialRootConnection?> TryOpenRootConnectionAsync(\n        string pipeName,\n        BetterGiInstanceType requestedType,\n        int? restartFromProcessId,\n        string[] args,\n        IReadOnlyList<TimeSpan> retryDelays,\n        TimeSpan connectTimeout,\n        CancellationToken cancellationToken)\n    {","sourceCodeStart":144,"sourceCodeEnd":180,"githubUrl":"https://github.com/babalae/better-genshin-impact/blob/a7cb36712dcb409be610257d877fcea3597e9d6b/BetterGenshinImpact/Service/Instance/InstanceBootstrap.cs#L144-L180","documentation":"Thrown during a restart handoff when Process.GetProcessById(restartFromProcessId).WaitForExit(15000) returns false — the previous BetterGI process (whose PID was passed via command line) did not exit within 15 seconds. The wait exists so the new instance can safely take over the root named pipe without colliding with the old one.","triggerScenarios":"The old process is hung (deadlock, blocking I/O, stuck modal dialog, or waiting on an unresponsive resource); or the supplied restartFromProcessId refers to a different long-lived process by coincidence.","commonSituations":"Previous BetterGI instance is blocked on a file lock, a stuck capture hook, or an unresponsive game; a debugger is attached to the old process preventing exit; the old process is mid-shutdown saving large state.","solutions":["Manually end the old BetterGI process (Task Manager → End Task, or `taskkill /PID <id> /F`) and retry.","Investigate why the old process cannot exit — check for modal dialogs, held locks, or attached debuggers.","If this recurs, the 15s window may be too short for slow shutdowns; review the shutdown path for blocking operations."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"try\n{\n    WaitForRestartSource(restartFromProcessId);\n}\ncatch (TimeoutException ex)\n{\n    logger.LogError(ex, \"Previous BetterGI process did not exit in time\");\n    // Offer the user a forced-kill option.\n}","preventionTips":["Before issuing a restart, gracefully close the old instance and confirm it exited.","If automating restarts, kill any lingering process by PID before relaunching.","Investigate shutdown-path blocking (held locks, modal dialogs) if this recurs."],"tags":["process","restart","timeout","instance","startup"],"backgroundTag":null,"analyzedSha":"a7cb36712dcb409be610257d877fcea3597e9d6b","analyzedAt":"2026-08-13T16:44:57.548Z","schemaVersion":2},"datasetVersion":"2026-08-13T19:17:28.613Z"}