{"record":{"id":"08a8ab0398992528","repo":"paperclipai/paperclip","slug":"refusing-to-reuse-postgresql-its-data-directory-belongs-to","errorCode":null,"errorMessage":"Refusing to reuse PostgreSQL: its data directory belongs to another instance.","messagePattern":"Refusing to reuse PostgreSQL: its data directory belongs to another instance\\.","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"server/src/index.ts","lineNumber":519,"sourceCode":"    const getRunningPid = (): number | null => {\n      if (!existsSync(postmasterPidFile)) return null;\n      try {\n        const pidLine = readFileSync(postmasterPidFile, \"utf8\").split(\"\\n\")[0]?.trim();\n        const pid = Number(pidLine);\n        if (!Number.isInteger(pid) || pid <= 0) return null;\n        if (!isPidRunning(pid)) return null;\n        return pid;\n      } catch {\n        return null;\n      }\n    };\n  \n    const runningPid = getRunningPid();\n    if (runningPid) {\n      port = embeddedPostgresOwnerPort(readFileSync(postmasterPidFile, \"utf8\"), dataDir, runningPid);\n      const actualDataDir = await getPostgresDataDirectory(`postgres://paperclip:paperclip@127.0.0.1:${port}/postgres`);\n      if (typeof actualDataDir !== \"string\" || resolve(actualDataDir) !== resolve(dataDir)) {\n        throw new Error(\"Refusing to reuse PostgreSQL: its data directory belongs to another instance.\");\n      }\n      logger.warn(`Embedded PostgreSQL already running; reusing existing process (pid=${runningPid}, port=${port})`);\n    } else {\n      const configuredAdminConnectionString = `postgres://paperclip:paperclip@127.0.0.1:${configuredPort}/postgres`;\n      try {\n        const actualDataDir = await getPostgresDataDirectory(configuredAdminConnectionString);\n        if (\n          typeof actualDataDir !== \"string\" ||\n          resolve(actualDataDir) !== resolve(dataDir)\n        ) {\n          throw new Error(\"reachable postgres does not use the expected embedded data directory\");\n        }\n        await ensurePostgresDatabase(configuredAdminConnectionString, \"paperclip\");\n        logger.warn(\n          `Embedded PostgreSQL appears to already be reachable without a pid file; reusing existing server on configured port ${configuredPort}`,\n        );\n      } catch {\n        const detectedPort = await detectPort(configuredPort);","sourceCodeStart":501,"sourceCodeEnd":537,"githubUrl":"https://github.com/paperclipai/paperclip/blob/3f1d897a7c018d76563a21c6e39c3c9b03933622/server/src/index.ts#L501-L537","documentation":"During startup, startServerWithDatabaseTeardown detects an already-running embedded PostgreSQL via its pid file and verifies — by querying `SHOW data_directory` through the running server — that the live process's data directory matches the configured one. If the resolved directories differ, it throws rather than reusing a PostgreSQL instance whose storage belongs to another Paperclip instance, preventing silent cross-instance data corruption.","triggerScenarios":"A postmaster pid file exists (runningPid truthy), embeddedPostgresOwnerPort accepted it, but getPostgresDataDirectory over postgres://paperclip:paperclip@127.0.0.1:<port>/postgres returns a non-string or a directory that does not resolve to the configured dataDir.","commonSituations":"Port collision avoidance made a previous instance's PostgreSQL take the port, and a new instance's stale pid file points at that foreign server; two clones of the repo sharing a data directory; a container restart where a leftover postgres from another mount is still listening.","solutions":["Shut down the foreign postgres process (find via `lsof -i :<port>` / `ps`), remove the stale pid file, and restart so this instance boots its own PostgreSQL","Give this instance a unique data directory if you intentionally run multiple instances","Reset the dev database (`rm -rf data/pglite && pnpm dev`) if the directory was copied from elsewhere","Verify PGDATA of the running server (`SELECT setting FROM pg_settings WHERE name='data_directory'`) matches your intended data dir"],"exampleFix":"# before: reusing another instance's postgres\npnpm dev\n// after: clear the foreign process/state, then start fresh\nkill <foreign-postgres-pid>; rm -rf data/pglite; pnpm dev","handlingStrategy":"try-catch","validationCode":"const actual = await getPostgresDataDirectory(adminConn);\nif (typeof actual !== \"string\" || resolve(actual) !== resolve(expectedDataDir)) {\n  throw new Error(\"running postgres data directory does not match; refusing to start\");\n}","typeGuard":"function dataDirMatches(actual: unknown, expected: string): actual is string {\n  return typeof actual === \"string\" && resolve(actual) === resolve(expected);\n}","tryCatchPattern":"try { await startServerWithDatabaseTeardown(...); }\ncatch (e) {\n  if (String(e.message).includes(\"data directory belongs to another instance\")) {\n    console.error(\"Stop the foreign postgres process and remove the stale pid file before restarting.\");\n  }\n  throw e;\n}","preventionTips":["Give each instance/clone its own data directory","Kill leftover postgres processes before restarting an instance","Check `lsof -i :<port>` for foreign listeners before startup","Avoid copying data/ directories between workspaces while servers run"],"tags":["postgres","embedded","data-directory","startup"],"backgroundTag":"invalid-state-transition","analyzedSha":"3f1d897a7c018d76563a21c6e39c3c9b03933622","analyzedAt":"2026-09-18T08:03:59.046Z","contentChangedAt":"2026-09-18T08:03:59.046Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}