paperclipai/paperclip · warning
CLI auth challenge was cancelled.
Error message
CLI auth challenge was cancelled.
What it means
Startup warning at server/src/index.ts:443. The pid file check found no live postmaster, but the server could still connect with the embedded credentials (postgres://paperclip:paperclip@127.0.0.1:<configuredPort>/postgres) and `SELECT current_setting('data_directory', true)` returned a path that resolves to the same embedded dataDir. That means a PostgreSQL server is already serving this exact cluster without a usable postmaster.pid, so the server reuses it and never constructs or starts a new EmbeddedPostgres instance.
Solutions
- Confirm the reused server is really yours: `psql "postgres://paperclip:paperclip@127.0.0.1:54329/postgres" -c "show data_directory;"` and match it against the log message.
- If the running server is stale or unwanted, stop it (`lsof -ti :54329 | xargs kill`) and restart the app so it owns the postgres lifecycle and regenerates postmaster.pid.
- Never delete postmaster.pid of a running cluster as a 'fix'; plan one clean restart to restore normal lock-file tracking.
- If the pid file lives on a mount that loses it, move the embedded data dir to stable local storage via database.embeddedPostgresDataDir.
Example fix
# before: pid file removed while embedded postgres still running rm -f ~/.paperclip/embedded-postgres/postmaster.pid # server logs: "...reachable without a pid file; reusing existing server on configured port 54329" # after: let the app own the lifecycle — stop the stray postgres, then start cleanly kill $(lsof -ti :54329) pnpm dev
Defensive patterns
Strategy: validation
Validate before calling
import { resolve } from "node:path";
import postgres from "postgres";
// Mirrors the server's probe: is the reachable server on this port OUR cluster?
export async function isExpectedEmbeddedServer(
adminUrl: string, // e.g. postgres://paperclip:paperclip@127.0.0.1:54329/postgres
expectedDataDir: string,
): Promise<boolean> {
const sql = postgres(adminUrl, { connect_timeout: 2 });
try {
const [row] = await sql`select current_setting('data_directory', true) as d`;
return typeof row?.d === "string" && resolve(row.d) === resolve(expectedDataDir);
} catch {
return false;
} finally {
await sql.end();
}
} Prevention
- Never delete postmaster.pid of a cluster that may be running; plan a clean restart instead.
- Keep the embedded data dir on stable local storage (not a mount that drops lock files).
- Avoid starting postgres manually against the embedded data dir; let the app own the lifecycle so the pid file stays authoritative.
When it happens
Trigger: postmaster.pid was deleted, truncated, or made unreadable while the embedded postgres kept running; the data dir is on a shared/volume mount where the pid file did not survive; an operator started postgres against this data dir manually/externally so this process never tracked its pid; a crashed supervisor removed the lock on restart.
Common situations: Partial cleanup of the data dir (rm of the lock file only) while the server was up; Docker/host volume mounts that exclude or lose the pid file; hand-run `postgres -D <dataDir>` during debugging; security tooling that deletes lock files.
Related errors
- Embedded bootstrap data directory does not match the…
- Cannot seed target embedded PostgreSQL at
- Embedded bootstrap requires its configured data directory
- Invalid embedded postmaster PID
- Invalid running embedded database port
AI-assisted analysis of paperclipai/paperclip@120ae5428f (2026-08-18).
Data as JSON: /api/errors/045fb9c23f7e5f9c.
Report an issue: GitHub.
Appendix: source
Thrown at cli/src/client/board-auth.ts:278
authorization: `Bearer ${challenge.boardApiToken}`,
},
},
);
setStoredBoardCredential({
apiBase,
token: challenge.boardApiToken,
userId: me.userId ?? me.user?.id ?? null,
storePath: params.storePath,
});
return {
token: challenge.boardApiToken,
approvalUrl,
userId: me.userId ?? me.user?.id ?? null,
};
}
if (status.status === "cancelled") {
throw new Error("CLI auth challenge was cancelled.");
}
if (status.status === "expired") {
throw new Error("CLI auth challenge expired before approval.");
}
await sleep(pollMs);
}
throw new Error("CLI auth challenge expired before approval.");
}
export async function revokeStoredBoardCredential(params: {
apiBase: string;
token: string;
}): Promise<void> {
const apiBase = normalizeApiBase(params.apiBase);
await requestJson<{ revoked: boolean }>(`${apiBase}/api/cli-auth/revoke-current`, {
method: "POST",View on GitHub (pinned to 120ae5428f)