paperclipai/paperclip · error

Embedded bootstrap data directory does not match the…

Error message

Embedded bootstrap data directory does not match the running postmaster

What it means

The script reads postmaster.pid from the configured data directory and verifies pidLines[1] (the data directory recorded by the postmaster) resolves to the same real path as the configured directory. A mismatch means the PID file belongs to a different instance — e.g. the data dir was moved, symlinked, or the config points at a copy — and the script refuses to touch that process.

Solutions

  1. Correct config.database.embeddedPostgresDataDir to match the directory the running postmaster actually uses (read pidLines[1] of postmaster.pid).
  2. Restart the dev server so postmaster.pid is regenerated for the current directory.
  3. Remove symlinks — configure the real resolved path.
  4. Delete a stale postmaster.pid only after confirming no postgres process is running, then restart.

Example fix

// before
embeddedPostgresDataDir: "./data/pglite-link" // symlink
// after
embeddedPostgresDataDir: "/abs/path/to/project/data/pglite" // realpath matching postmaster.pid line 2
Defensive patterns

Strategy: validation

Validate before calling

const pid = readFileSync(path.join(dataDir, "postmaster.pid"), "utf8").split(/\r?\n/);
if (realpathSync(pid[1] ?? "") !== realpathSync(dataDir)) throw new Error("postmaster belongs to a different data dir");

Try / catch

try { await main(); } catch (e) {
  if (e.message.includes("does not match the running postmaster")) { console.error("restarting dev server to regenerate postmaster.pid"); }
  throw e;
}

Prevention

When it happens

Trigger: realpathSync(pidLines[1]) !== realpathSync(dataDir) while reading <dataDir>/postmaster.pid in non-postgres mode.

Common situations: Configured embeddedPostgresDataDir moved/renamed after the server started; data dir accessed via a symlink while postmaster recorded the real path; two dev instances and the config points at the wrong one; stale postmaster.pid copied with the directory.

Understand the failure class

Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.

Related errors


AI-assisted analysis of paperclipai/paperclip@3f1d897a7c (2026-09-18). Data as JSON: /api/errors/0148e8a81ab1e04d. Report an issue: GitHub.

Appendix: source

Thrown at packages/db/scripts/create-auth-bootstrap-invite.ts:45

    throw new Error("Usage: tsx create-auth-bootstrap-invite.ts --config <path> --base-url <url>");
  }

  const config = JSON.parse(readFileSync(path.resolve(configPath), "utf8")) as {
    database?: {
      mode?: string;
      embeddedPostgresPort?: number;
      embeddedPostgresDataDir?: string;
      connectionString?: string;
    };
  };
  // The server can select another port when the configured one is occupied.
  // Bind bootstrap to this data directory's running process, never another instance.
  let embeddedPort: number | undefined;
  if (config.database?.mode !== "postgres") {
    const dataDir = config.database?.embeddedPostgresDataDir;
    if (!dataDir) throw new Error("Embedded bootstrap requires its configured data directory");
    const pidLines = readFileSync(path.join(dataDir, "postmaster.pid"), "utf8").split(/\r?\n/);
    if (realpathSync(pidLines[1] ?? "") !== realpathSync(dataDir)) throw new Error("Embedded bootstrap data directory does not match the running postmaster");
    const postmasterPid = Number(pidLines[0]);
    if (!Number.isInteger(postmasterPid) || postmasterPid <= 1) throw new Error("Invalid embedded postmaster PID");
    process.kill(postmasterPid, 0);
    embeddedPort = Number(pidLines[3]);
    if (!Number.isInteger(embeddedPort) || embeddedPort < 1 || embeddedPort > 65535) throw new Error("Invalid running embedded database port");
  }
  const dbUrl =
    config.database?.mode === "postgres"
      ? config.database.connectionString
      : `postgres://paperclip:paperclip@127.0.0.1:${embeddedPort}/paperclip`;
  if (!dbUrl) {
    throw new Error(`Could not resolve database connection from ${configPath}`);
  }

  const db = createDb(dbUrl);
  const closableDb = db as typeof db & {
    $client?: {
      end?: (options?: { timeout?: number }) => Promise<void>;

View on GitHub (pinned to 3f1d897a7c)