paperclipai/paperclip · error
Invalid embedded postmaster PID
Error message
Invalid embedded postmaster PID
What it means
This error is thrown by the create-auth-bootstrap-invite bootstrap script when it parses the embedded PostgreSQL postmaster.pid file and the first line (the PID) is not a usable integer greater than 1. The script must signal the running postmaster (process.kill(pid, 0)) to prove the embedded database is alive before binding the invite creation to it; a malformed PID means the pid file is corrupt, empty, or from a different format, so it refuses to continue rather than killing or connecting to the wrong process.
Solutions
- Ensure the embedded Postgres for this data directory is actually running (start the dev server with `pnpm dev`) before running the bootstrap script
- Stop everything, delete the stale postmaster.pid (or reset the data dir with `rm -rf data/pglite && pnpm dev` if acceptable), and let a fresh postmaster rewrite it
- Verify the config's database.embeddedPostgresDataDir points at the real live data directory, not a copy, backup, or empty folder
- If you need no embedded database, set database.mode to "postgres" with a connectionString so the pid file is not consulted at all
Example fix
// before (config.json)
{ "database": { "mode": "embedded", "embeddedPostgresDataDir": "./backup/pglite-copy" } }
// after
{ "database": { "mode": "embedded", "embeddedPostgresDataDir": "./data/pglite" } }
// and start the embedded database first:
// pnpm dev Defensive patterns
Strategy: validation
Validate before calling
import { readFileSync } from "node:fs";
const pidLines = readFileSync(`${dataDir}/postmaster.pid`, "utf8").split(/\r?\n/);
const pid = Number(pidLines[0]);
if (!Number.isInteger(pid) || pid <= 1) throw new Error("postmaster.pid has no valid PID; start the embedded database first");
process.kill(pid, 0); // throws ESRCH if the process is gone Type guard
function isValidPid(v: unknown): v is number {
return typeof v === "number" && Number.isInteger(v) && v > 1;
} Try / catch
try {
await runBootstrapInvite(configPath, baseUrl);
} catch (err) {
if (err instanceof Error && err.message === "Invalid embedded postmaster PID") {
// regenerate the pid file, then retry once
await startEmbeddedPostgres(dataDir);
await runBootstrapInvite(configPath, baseUrl);
} else throw err;
} Prevention
- Always start the dev server (pnpm dev) so embedded Postgres writes a fresh postmaster.pid before running bootstrap scripts
- Never hand-edit or copy postmaster.pid; treat the data directory as owned by the postmaster
- Reset expendable embedded data dirs (rm -rf data/pglite) instead of restoring partial backups
- If you do not need embedded mode, configure database.mode=postgres with a connectionString
When it happens
Trigger: Running `tsx create-auth-bootstrap-invite.ts --config <path> --base-url <url>` against a config whose database.mode is not "postgres" (embedded mode) where the file <embeddedPostgresDataDir>/postmaster.pid exists but its first line, when passed to Number(), is NaN, non-integer, zero, or negative (or 1).
Common situations: Embedded Postgres was never started so an empty or leftover postmaster.pid exists; the data directory was copied or restored and the pid file is truncated; a stale/corrupt pid file left after an unclean shutdown; the config points at a directory that contains a partial or hand-edited postmaster.pid; the database mode was switched between embedded and postgres without cleaning the data dir.
Understand the failure class
Background: "Invalid value" and "allowed values are" config errors: what your library rejected and how to fix it — this error's family across 41 libraries.
Related errors
- Embedded bootstrap data directory does not match the…
- Embedded bootstrap requires its configured data directory
- Invalid running embedded database port
- Could not resolve database connection from
- database-contract-unmet
AI-assisted analysis of paperclipai/paperclip@3f1d897a7c (2026-09-18).
Data as JSON: /api/errors/964d3545081db738.
Report an issue: GitHub.
Appendix: source
Thrown at packages/db/scripts/create-auth-bootstrap-invite.ts:47
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)