babalae/better-genshin-impact · error · TimeoutException

等待旧 BetterGI 进程 {restartFromProcessId.Value} 退出超时。

Error message

等待旧 BetterGI 进程 {restartFromProcessId.Value} 退出超时。

What it means

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.

Source

Thrown at BetterGenshinImpact/Service/Instance/InstanceBootstrap.cs:162

    public void Dispose()
    {
        Interlocked.Exchange(ref _firstServer, null)?.Dispose();
        Interlocked.Exchange(ref _firstRootConnection, null)?.Client.Dispose();
    }

    private static void WaitForRestartSource(int? restartFromProcessId)
    {
        if (restartFromProcessId is null || restartFromProcessId == Environment.ProcessId)
        {
            return;
        }

        try
        {
            using var process = Process.GetProcessById(restartFromProcessId.Value);
            if (!process.WaitForExit(milliseconds: 15_000))
            {
                throw new TimeoutException(
                    $"等待旧 BetterGI 进程 {restartFromProcessId.Value} 退出超时。");
            }
        }
        catch (ArgumentException)
        {
            // 旧进程已经退出。
        }
    }

    private static async Task<InitialRootConnection?> TryOpenRootConnectionAsync(
        string pipeName,
        BetterGiInstanceType requestedType,
        int? restartFromProcessId,
        string[] args,
        IReadOnlyList<TimeSpan> retryDelays,
        TimeSpan connectTimeout,
        CancellationToken cancellationToken)
    {

View on GitHub (pinned to a7cb36712d)

Solutions

  1. Manually end the old BetterGI process (Task Manager → End Task, or `taskkill /PID <id> /F`) and retry.
  2. Investigate why the old process cannot exit — check for modal dialogs, held locks, or attached debuggers.
  3. If this recurs, the 15s window may be too short for slow shutdowns; review the shutdown path for blocking operations.
Defensive patterns

Strategy: try-catch

Try / catch

try
{
    WaitForRestartSource(restartFromProcessId);
}
catch (TimeoutException ex)
{
    logger.LogError(ex, "Previous BetterGI process did not exit in time");
    // Offer the user a forced-kill option.
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of babalae/better-genshin-impact@a7cb36712d (2026-08-13). Data as JSON: /api/errors/67e86a2e850be644. Report an issue: GitHub.