# 3D000 — invalid_catalog_name

> PostgreSQL SQLSTATE 3D000: invalid_catalog_name, source-backed diagnosis and recovery guidance.
---

# 3D000

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

SQLSTATE `3D000` is **invalid_catalog_name** in Class `3D`. `3D000` identifies an invalid database/catalog name. It can occur before startup completes when a requested database is absent, and fixed `dbcommands.c` paths also emit it while a SQL command resolves a missing database/template. The selected startup case reports C=`3D000`, S=`FATAL`, `database "<generated>" does not exist`; that is one phase, not the whole class.

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

| Field | Value |
| --- | --- |
| SQLSTATE | `3D000` |
| Condition | `invalid_catalog_name` |
| 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_INVALID_CATALOG_NAME, ERRCODE_UNDEFINED_DATABASE` |
| Aliases | `ERRCODE_UNDEFINED_DATABASE` |

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

## Meaning {#meaning}

The code covers database/catalog resolution in more than one phase. `postinit.c` emits the selected startup FATAL when the requested database does not exist, while `dbcommands.c` uses the same condition for SQL commands that resolve a missing database or template. The startup record is therefore one concrete producer, not a universal description of every 3D000.

Treat the phase as part of the diagnosis. A startup DSN for a missing database produces a server `FATAL` and no session-level transaction; a SQL command such as an administrative database/template lookup can produce an `ERROR` inside an existing session, where normal rollback or savepoint recovery applies. The code alone does not tell you which phase failed.

### Phase guide {#scenarios}

| Phase | Fixed producer | Recovery boundary |
| --- | --- | --- |
| Startup database selection | `postinit.c` reports `database "%s" does not exist` as `FATAL`. | Fix the DSN or create/rename the database from a control database, then make a fresh connection. There is no session to roll back. |
| SQL command or template lookup | `dbcommands.c` uses `3D000` while handling an administrative command. | Inspect the command and its transaction; an `ERROR` inside an explicit transaction requires `ROLLBACK` or an intentional savepoint before unrelated work. |

## Diagnosis {#diagnosis}

Check the database name, whether it was renamed or dropped, the command phase, and which control database is available. The selected `collector` is a raw server ErrorResponse record, not csvlog/jsonlog; raw startup and psycopg are independent attempts. The selected runs use independent startup attempts: psycopg reports driver SQLSTATE `null` and no transaction, while the raw server ErrorResponse and a fresh known-database probe establish the server code and recovery path. Do not confuse a missing database with 3F000 schema lookup.

For startup failures, compare the exact database parameter after client-side defaults, URL decoding, and environment expansion; then inspect the raw server `C`, `S`, and `M` fields. For an already-connected SQL command, capture the command text and transaction state separately. The successful control-database probe proves only that a new session can be established, not that the original missing-database attempt had a transaction.

## Response {#response}

Use an existing control database to inspect or create the intended database, or correct the DSN/command target, then establish a new connection. A pre-session failure has no session-level `ROLLBACK`; SQL command paths must be repaired in their owning administrative transaction and rechecked against the intended catalog.

If the database was intentionally removed, repair the application target or migration rather than retrying the same startup request. If an administrative command failed after a session existed, recover that transaction before issuing catalog changes. Reconcile any work sent by another connection before repeating it; the startup failure itself did not execute SQL in the missing database.

## Observed diagnostics {#messages}

The selected startup branch emits `FATAL` with primary `database "%s" does not exist`; the database name is dynamic. Fixed SQL-command paths use the same SQLSTATE but can have different surrounding operation and transaction boundaries. A client-side startup exception without the server ErrorResponse is insufficient.

The selected name `c3d000_missing_cff0e8d38be4` is generated by the run. The server ErrorResponse is `C=3D000`, `S=FATAL`; psycopg's startup `sqlstate=null` and the known-database probe are separate attempts, not additional views of one session.

## Representative case {#case}

<!-- BEGIN SQLSTATE SNIPPET: invalid_database -->

The registry contains the probe used after the startup ErrorResponse. The trigger uses a generated database name on a fresh startup connection, so no SQL statement can reproduce that pre-session failure.

```sql
SELECT 1;
```

<!-- END SQLSTATE SNIPPET -->

The selected 18.6 server ErrorResponse is C=`3D000`, S=`FATAL`, `database "c3d000_missing_cff0e8d38be4" does not exist`. The driver startup diagnostic has SQLSTATE `null`; the failed connection has no transaction, while the known-good probe returned `1` and `IDLE`.

The downloadable case and evidence projections are [`3D000 case JSON`](../data/cases/3d000.json) and [`authored evidence`](../data/evidence/3d000.json). The runner manifest is `verify/cases/3D000/cases.json`; the page SQL is checked against its shared registry before publication.

## Versions {#versions}

The generated facts table records the locked catalogue snapshots and earliest observed definition. The selected natural runtime scope is PostgreSQL 18.6 and 10.21; this does not infer behavior for every intermediate release.

## Related {#related}

- [`3F000` — invalid_schema_name](../3f000/)
- [`28000` — invalid_authorization_specification](../28000/)

## Sources {#sources}

- `src.postinit-missing-db.18.6` — `src/backend/utils/init/postinit.c` at `REL_18_6` commit `724edf9bde9d356724ad384a2e196edc3c9f80f7`; fixed blob SHA-256 `abcf80637b153d3aa6f0d2b600943af0a2b4d9a393d05a844db91f8b6b66d0d7` ([source](https://github.com/postgres/postgres/blob/724edf9bde9d356724ad384a2e196edc3c9f80f7/src/backend/utils/init/postinit.c#L1014-L1017)).
- `src.postinit-missing-db.10.23` — `src/backend/utils/init/postinit.c` at `REL_10_23` commit `02991e79f8f58bc208f05dcc8af0c62dbe0a6ea4`; fixed blob SHA-256 `be912628b29f34afc7b7fbc9d2f480c5ac9cb6ce013a1d992fc7415a311a95ed` ([source](https://github.com/postgres/postgres/blob/02991e79f8f58bc208f05dcc8af0c62dbe0a6ea4/src/backend/utils/init/postinit.c#L846-L848)).
- `src.dbcommands-missing-database.18.6` — `src/backend/commands/dbcommands.c` at `REL_18_6` commit `724edf9bde9d356724ad384a2e196edc3c9f80f7`; fixed blob SHA-256 `64b77d2197dce16eb1ad19d8168382459d621a03df0b00be157f7db39ca22acf` ([source](https://github.com/postgres/postgres/blob/724edf9bde9d356724ad384a2e196edc3c9f80f7/src/backend/commands/dbcommands.c#L1702-L1704)).
- `src.dbcommands-missing-database.10.23` — `src/backend/commands/dbcommands.c` at `REL_10_23` commit `02991e79f8f58bc208f05dcc8af0c62dbe0a6ea4`; fixed blob SHA-256 `cf9e069415deb7d50ef931048616ae7f9a459d6db64e3d54a0062e8334970ce6` ([source](https://github.com/postgres/postgres/blob/02991e79f8f58bc208f05dcc8af0c62dbe0a6ea4/src/backend/commands/dbcommands.c#L806-L808)).
- `src.calls.REL_18_6` / `src.calls.REL_10_23` — fixed local call scans, SHA-256 `9ee8a0e81d8f0825c5c1ae45583439859a26e602bdd4ce2f2a62aa278867ccbf` / `00d16d3eb01b71ccf1b245c8f3102f9d0ec9f36fb02777b8dd1b99fcb263040c`; these scans preserve the resolved call context used by the claims.
