paperclipai/paperclip · warning

Request failed

Error message

Request failed: ${response.status}

What it means

A startup warning (logger.warn) from Paperclip's embedded-PostgreSQL bootstrap in server/src/index.ts:431, not a thrown error. When DATABASE_URL is unset, the server resolves the embedded data dir (config.embeddedPostgresDataDir) and reads its postmaster.pid: if the first line parses to a positive integer pid and that pid is alive (checked with process.kill(pid, 0)), another PostgreSQL postmaster already owns this cluster. The server deliberately skips init/start and attaches to the existing process instead of launching a second postmaster against the same data directory (which would corrupt it).

Solutions

  1. If a second server against the same DB is intentional, no action is needed - the server reuses the running postgres; note the pid and port from the message.
  2. Otherwise stop the leftover postgres: verify with `ps -p <pid> -o comm=` then `kill <pid>` (or `lsof -ti :54329 | xargs kill`), and restart the server so it manages the lifecycle itself.
  3. For parallel instances, give each its own data dir and port via config database.embeddedPostgresDataDir / database.embeddedPostgresPort, or set DATABASE_URL to an external postgres.
  4. For a clean-slate dev cluster, stop all processes using the data dir, then delete the embedded data dir so the next start re-runs initdb (migrations re-apply automatically on first run).

Example fix

// before: two dev servers share the default embedded data dir
// second start logs: "Embedded PostgreSQL already running; reusing existing process (pid=1234, port=54329)"

// after (paperclip.config.json for the second instance): dedicated data dir + port
{
  "database": {
    "embeddedPostgresDataDir": "~/.paperclip/embedded-postgres-2",
    "embeddedPostgresPort": 54330
  }
}
Defensive patterns

Strategy: validation

Validate before calling

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

const isPidAlive = (pid: number): boolean => {
  try { process.kill(pid, 0); return true; } catch { return false; }
};

// Run before starting a second server against the same data dir
export function embeddedPostgresRunning(dataDir: string): number | null {
  const pidFile = resolve(dataDir, "postmaster.pid");
  if (!existsSync(pidFile)) return null;
  const pidLine = readFileSync(pidFile, "utf8").split("\n")[0]?.trim();
  const pid = Number(pidLine);
  return Number.isInteger(pid) && pid > 0 && isPidAlive(pid) ? pid : null;
}

Type guard

function isEmbeddedPostgresLive(dataDir: string): boolean {
  const pid = embeddedPostgresRunning(dataDir); // helper above
  return typeof pid === "number"; // true => a second start will reuse it
}

Prevention

When it happens

Trigger: Running `pnpm dev` (or any server start without DATABASE_URL) while a previous Paperclip server process or its orphaned embedded postgres child is still alive and using the same embeddedPostgresDataDir; e.g. the API server was killed but the postgres child survived, or a second checkout/worktree was started with the default data dir and port 54329.

Common situations: Dev loop where Ctrl-C on the server leaves the embedded postgres child running; two worktrees or two terminals sharing the default embedded data dir; CI runners that reuse a persisted data dir between jobs; a manually started postgres pointed at the same data directory.

Related errors


AI-assisted analysis of paperclipai/paperclip@120ae5428f (2026-08-18). Data as JSON: /api/errors/023c67b1bcadb5a6. Report an issue: GitHub.

Appendix: source

Thrown at cli/src/client/board-auth.ts:171

  if (init?.body !== undefined && !headers.has("content-type")) {
    headers.set("content-type", "application/json");
  }
  if (!headers.has("accept")) {
    headers.set("accept", "application/json");
  }

  const response = await fetch(url, {
    ...init,
    headers,
  });

  if (!response.ok) {
    const body = await response.json().catch(() => null);
    const message =
      body && typeof body === "object" && typeof (body as { error?: unknown }).error === "string"
        ? (body as { error: string }).error
        : `Request failed: ${response.status}`;
    throw new Error(message);
  }

  return response.json() as Promise<T>;
}

export async function openUrl(url: string): Promise<boolean> {
  const { command, args } =
    process.platform === "darwin"
      ? { command: "open", args: [url] }
      : process.platform === "win32"
        ? { command: "cmd", args: ["/c", "start", "", url] }
        : { command: "xdg-open", args: [url] };

  return new Promise<boolean>((resolve) => {
    let child: ChildProcess;
    try {
      child = spawn(command, args, { detached: true, stdio: "ignore" });
    } catch {

View on GitHub (pinned to 120ae5428f)