# F0001 — lock_file_exists

> Source-backed full reference for PostgreSQL SQLSTATE F0001.
---

# F0001

## At a glance {#at-a-glance}

`F0001` is a startup ownership failure. PostgreSQL checks both the lock file and the shared-memory identity for the intended data directory; a live owner is different from a stale artifact left by a crashed postmaster.

<!-- BEGIN SQLSTATE FACTS: generated by scripts/generate.py; do not edit -->

| Field | Value |
| --- | --- |
| SQLSTATE | `F0001` |
| Condition | `lock_file_exists` |
| Status | `active` |
| Known present by | `7.4` |
| Locked snapshots | `9.0.23, 9.1.24, 9.2.24, 9.3.25, 9.4.26, 9.5.25, 9.6.24, 10.23, 11.22, 12.22, 13.23, 14.24, 15.19, 16.15, 17.11, 18.6, 19beta3` |
| Macros | `ERRCODE_LOCK_FILE_EXISTS` |
| Aliases | `—` |

<!-- source facts: data/errcodes/F0001.json -->
<!-- END SQLSTATE FACTS -->

## Meaning {#meaning}

`CreateLockFile` creates the lock file atomically with `O_EXCL`. If an existing file contains a PID, it uses `kill(pid, 0)` to distinguish a live process from a vanished one. For the data-directory lock it also reads the recorded shared-memory identifiers and calls `PGSharedMemoryIsInUse`; that helper only treats a segment associated with the intended `DataDir` as relevant, because accidental shared-memory ID matches are possible. An empty lock file is a separate F0001 branch and can mean that another server is still starting or that a previous startup crashed.

The SysV shared-memory startup path has the same boundary: an attached or indeterminate pre-existing segment is reported as still in use, while an unattached segment associated with the data directory can be removed and recreated. These guards prevent two postmasters from claiming one cluster.

## Messages {#messages}

- **FATAL**, SQLSTATE `F0001`: `lock file "%s" already exists`.
  - Data-directory hint: `Is another postgres (PID %d) running in data directory "%s"?` (the postmaster wording is selected for the encoded owner).
  - Socket-lock variants name `using socket file "%s"` instead.
- **FATAL**, SQLSTATE `F0001`: `lock file "%s" is empty`.
  - Hint: `Either another server is starting, or the lock file is the remnant of a previous server startup crash.`
- **FATAL**, SQLSTATE `F0001`: `pre-existing shared memory block (key %lu, ID %lu) is still in use`.
  - Hint: `Terminate any old server processes associated with data directory "%s".`

Other lock-file I/O failures use `errcode_for_file_access()` and can receive a different SQLSTATE; do not collapse them into `F0001`.

## Diagnosis {#diagnosis}

Confirm the exact `PGDATA`, socket directory, PID, start time, recorded device/inode identity, process owner, and postmaster log. A successful `kill(pid, 0)`, or a failure whose `errno` is neither `ESRCH` nor `EPERM`, keeps the live-process branch active. `ESRCH` means that the PID does not exist; `EPERM` means that a process exists but the caller lacks permission to signal it. This guard treats `EPERM` as a candidate for the next identity checks because `checkDataDir()` and the 0600/0640 lock-file modes rule out a different-UID process as the competing postmaster; it does not by itself authorize deleting the lock. If the PID is gone or treated as a different-UID candidate, inspect the data-directory shared-memory IDs and whether an orphan backend still attaches before considering cleanup. An empty file is explicitly ambiguous, not proof of staleness.

## Response {#response}

This is a startup **FATAL**, so there is no client transaction state to recover; after the ownership problem is resolved, start the server and use a new connection. If another postmaster or backend is live, stop or coordinate it through normal administration. Only after verifying the exact cluster, process identity, and shared-memory state may an operator follow the documented stale-lock cleanup path. Never remove a lock or shared-memory object by pathname, PID text, or numeric ID alone; if the owner is live, deletion risks split-brain (two postmasters claiming one cluster) and data loss.

## Versions {#versions}

The locked catalogue records this condition from 7.4; fixed startup and shared-memory paths are from PostgreSQL 18.6. No process, lock-file, or shared-memory fault was induced for this source-only entry.

## Related {#related}

[`F0000`](../f0000/), [`57P03`](../57p03/), [`58P01`](../58p01/)

## Sources {#sources}

[`src/backend/utils/init/miscinit.c#L1262-L1427`](https://github.com/postgres/postgres/blob/724edf9bde9d356724ad384a2e196edc3c9f80f7/src/backend/utils/init/miscinit.c#L1262)

[`src/backend/port/sysv_shmem.c#L306-L335`](https://github.com/postgres/postgres/blob/724edf9bde9d356724ad384a2e196edc3c9f80f7/src/backend/port/sysv_shmem.c#L306)

[`src/backend/port/sysv_shmem.c#L765-L835`](https://github.com/postgres/postgres/blob/724edf9bde9d356724ad384a2e196edc3c9f80f7/src/backend/port/sysv_shmem.c#L765)

The structured [evidence record](../data/evidence/f0001.json) records live/stale guards, fixed FATAL messages, and the source/runtime boundary.
