moeru-ai/airi · error · Error

Previous Godot stage process is still shutting down. Retry a

Error message

Previous Godot stage process is still shutting down. Retry after it exits.

What it means

Thrown by the start handler inside the lifecycle mutex after it detected an existing currentProcess in a non-'starting' state, called stopProcessAfterFailedStart to tear it down, and then observed currentProcess === activeProcess — meaning the old process handle did not clear during shutdown. The start refuses to proceed to avoid double-process state.

Source

Thrown at apps/stage-tamagotchi/src/main/services/airi/godot-stage/index.ts:718

      return await lifecycleMutex.runExclusive(async () => {
        let spawnedProcess: GodotStageProcess | undefined

        try {
          if (currentProcess && currentStatus.state === 'running') {
            return currentStatus
          }

          if (currentProcess && currentStatus.state === 'starting' && currentReady) {
            await currentReady.promise
            return currentStatus
          }

          if (currentProcess) {
            const activeProcess = currentProcess
            await stopProcessAfterFailedStart()

            if (currentProcess === activeProcess) {
              throw new Error('Previous Godot stage process is still shutting down. Retry after it exits.')
            }
          }

          await stopSocketRuntime()

          const socketRuntime = await startSocketRuntime()
          const godotBinary = await resolveGodotBinary()
          const websocketUrl = `ws://127.0.0.1:${socketRuntime.port}/ws?token=${socketRuntime.token}`
          const readyDeferred = createDeferred<void>()
          const readyTimeout = setTimeout(() => {
            readyDeferred.reject(new Error('Godot stage did not report ready in time.'))
          }, 20_000)

          currentReady = readyDeferred
          expectedProcessExit = false
          setStatus({
            state: 'starting',
            pid: null,

View on GitHub (pinned to 27111382b4)

Solutions

  1. Retry electronGodotStageStart after the previous process actually exits (its pid disappears from the process table).
  2. If the Godot child is wedged, kill it externally, then restart.
  3. Ensure stopProcessAfterFailedStart fully awaits process exit and clears currentProcess; if it does not, report it as a lifecycle bug.
  4. Avoid hammering start while a shutdown is in flight; debounce or disable the control until status returns to stopped.
Defensive patterns

Strategy: retry

Try / catch

try {
  await electronGodotStageStart()
}
catch (error) {
  if (errorMessageFrom(error)?.startsWith('Previous Godot stage process is still shutting down')) {
    // wait until status returns 'stopped' and the old pid is gone, then retry
  }
}

Prevention

When it happens

Trigger: Calling electronGodotStageStart while a previous Godot process handle is still assigned to currentProcess (e.g. a failed prior start whose exit handler hasn't fired), and stopProcessAfterFailedStart did not null out currentProcess before the equality check.

Common situations: Repeated start attempts after a failed launch; the Godot child process ignoring SIGTERM and lingering; exit handler that clears currentProcess racing with the restart; rapid start/stop toggling in the UI.

Related errors


AI-assisted analysis of moeru-ai/airi@27111382b4 (2026-08-12). Data as JSON: /api/errors/f2f23e15971fb8ef. Report an issue: GitHub.