paperclipai/paperclip · critical · Error

Refusing embedded PostgreSQL recovery because the data direc

Error message

Refusing embedded PostgreSQL recovery because the data directory reports a live process (pid=${runningPostgresPid})

What it means

When embedded PostgreSQL exits unexpectedly, the supervisor tries to recover by restarting it, but first re-checks the postmaster.pid in the data directory. If the pid recorded there is still a live process (process.kill(pid, 0) succeeds), restarting could corrupt a data directory that another running postmaster owns, so recovery is refused. This is a safety stop, not a transient failure.

Source

Thrown at server/src/index.ts:572

            });
          }
        } else {
          logger.info(`Embedded PostgreSQL cluster already exists (${clusterVersionFile}); skipping init`);
        }

        if (existsSync(postmasterPidFile)) {
          logger.warn("Removing stale embedded PostgreSQL lock file");
          rmSync(postmasterPidFile, { force: true });
        }
        try {
          await embeddedPostgres.start();
        } catch (err) {
          logEmbeddedPostgresFailure("start", err);
          throw formatEmbeddedPostgresError(err, {
            fallbackMessage: `Failed to start embedded PostgreSQL on port ${port}`,
            recentLogs: logBuffer.getRecentLogs(),
          });
        }
        embeddedPostgresStartedByThisProcess = true;
        embeddedPostgresSupervisor = createEmbeddedPostgresSupervisor({
          initialInstance: embeddedPostgres,
          createInstance: createEmbeddedPostgres,
          beforeRestart: () => {
            const runningPostgresPid = getRunningPid();
            if (runningPostgresPid) {
              throw new Error(`Refusing embedded PostgreSQL recovery because the data directory reports a live process (pid=${runningPostgresPid})`);
            }
            if (existsSync(postmasterPidFile)) rmSync(postmasterPidFile, { force: true });
          },
          onUnexpectedExit: (code, signal) => logger.error(
            { code, signal, recentLogs: logBuffer.getRecentLogs() },
            "Embedded PostgreSQL exited unexpectedly; attempting recovery",
          ),
          onRestartAttemptFailed: (err, attempt) => logger.error(
            { err, attempt, recentLogs: logBuffer.getRecentLogs() },
            "Embedded PostgreSQL recovery attempt failed",

View on GitHub (pinned to 01ad858492)

Solutions

  1. Identify the process: `ps -p <pid> -o pid,ppid,command` — confirm whether it is really a postgres postmaster.
  2. If it is a leftover Paperclip/postgres you own, shut it down gracefully (`pg_ctl stop -D <dataDir>` or stop the other Paperclip process), then restart the server so recovery can proceed.
  3. If the pid belongs to an unrelated process (pid reuse), verify no postgres owns the data dir, then remove the stale postmaster.pid manually and restart.
  4. Prevent recurrence: run only one embedded-mode server per data directory; prefer a managed Postgres via DATABASE_URL for parallel dev servers.

Example fix

# before: recovery refused, pid 4242 alive
ps -p 4242 -o pid,command   # leftover postmaster from a killed server
pg_ctl stop -D ~/.paperclip/embedded-pg/data || kill 4242

# after: pid gone, restart the Paperclip server; supervisor recovery proceeds
rm -f ~/.paperclip/embedded-pg/data/postmaster.pid   # only if ps shows no postgres
paperclip dev
Defensive patterns

Strategy: fallback

Validate before calling

import { existsSync, readFileSync } from "node:fs";
import path from "node:path";

function pidAlive(pid: number): boolean {
  try { process.kill(pid, 0); return true; } catch { return false; }
}

function embeddedPgSafeToStart(dataDir: string): boolean {
  const pidFile = path.join(dataDir, "postmaster.pid");
  if (!existsSync(pidFile)) return true;
  const pid = Number(readFileSync(pidFile, "utf8").split("\n")[0]);
  return !Number.isInteger(pid) || pid <= 0 || !pidAlive(pid);
}

Type guard

const isPostmasterPidStale = (dataDir: string): boolean => {
  const pidFile = path.join(dataDir, "postmaster.pid");
  if (!existsSync(pidFile)) return false;
  const pid = Number(readFileSync(pidFile, "utf8").split("\n")[0]);
  try { process.kill(pid, 0); return false; } catch { return true; }
};

Prevention

When it happens

Trigger: beforeRestart hook in createEmbeddedPostgresSupervisor (server startup with no DATABASE_URL, embedded PG mode): embedded PG crashed or 'exited' from this process's view while a postmaster with the pid in postmaster.pid is still alive — e.g. a forked/orphaned postmaster, another Paperclip server instance sharing the data dir, or the pid got reused by an unrelated live process.

Common situations: Two dev servers started against the same PGlite/embedded data directory; a previous server killed with SIGKILL leaving the postmaster alive detached; container/PID-namespace weirdness where the recorded host pid maps to a live process; pid reuse on long-running machines after an unclean shutdown.

Related errors


AI-assisted analysis of paperclipai/paperclip@01ad858492 (2026-08-21). Data as JSON: /api/errors/55b4aa32b84323ed. Report an issue: GitHub.