# 2BP01 — dependent_objects_still_exist

> PostgreSQL SQLSTATE 2BP01: dependency-aware DROP diagnostics and repair.
---

# 2BP01 — dependent_objects_still_exist

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

`2BP01` means a DROP or related catalog operation would remove an object that other database objects still require. The useful repair is dependency-aware: identify the dependent object, decide whether it should be removed or retained, and only then retry the original operation.

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

| Field | Value |
| --- | --- |
| SQLSTATE | `2BP01` |
| Condition | `dependent_objects_still_exist` |
| 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_DEPENDENT_OBJECTS_STILL_EXIST` |
| Aliases | `—` |

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

## Meaning {#meaning}

The dependency walker emits `2BP01` for a blocked object operation. In the representative path, a view depends on a table, so `DROP TABLE` cannot proceed. The primary message, DETAIL, and CASCADE hint are dynamically assembled from object descriptions and the dependency graph. `2BP01` is about dependent objects; `2B000` is a separate dependent-privilege-descriptor condition.

## Diagnosis {#diagnosis}

Capture SQLSTATE, primary message, DETAIL, and HINT together. The DETAIL identifies the dependent view in the selected path. Inspect the view definition and dependency metadata before choosing a repair; `pg_depend` and `pg_get_viewdef()` help explain why the object is retained. In the selected explicit `BEGIN` block, this ERROR puts the connection in `INERROR` until `ROLLBACK`; an autocommit statement has no surrounding block to preserve. Catalog queries issued through the failed explicit block will not provide a clean diagnosis.

## Response {#response}

Rollback the failed explicit transaction. If the view is intentionally disposable, drop that view first and then the table; if it is part of the schema contract, preserve it and choose a different migration. `CASCADE` is a deliberate request to remove dependent objects and can exceed the intended change, so the HINT is not an instruction to apply it automatically. After a dependency-aware repair, rerun the complete DDL plan and verify the surviving objects.

## Messages {#messages}

The selected source branch uses `cannot drop %s because other objects depend on it`, a dynamic internal DETAIL, and `Use DROP ... CASCADE to drop the dependent objects too.` The object descriptions and dependency list are runtime values; do not treat the DETAIL as a stable single-object template or assume every 2BP01 path has this exact wording.

## Representative case {#case}

The runner reads this dependency sequence from `verify/cases/2BP01/snippets.json` (SHA-256 `899e4fd9fb002fc293bd9efee0204e4f2622968c6d9460ad05eb9a0ebd3bac69`). It uses an explicit `BEGIN`/`ROLLBACK` for the failed DROP and then removes the known view before its table, without CASCADE. See [the public case export](../data/cases/2bp01.json) and [structured evidence](../data/evidence/2bp01.json).

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

```sql
CREATE TABLE base_items(id integer PRIMARY KEY, payload text NOT NULL);
CREATE VIEW dependent_view AS SELECT id, payload FROM base_items;
BEGIN;
DROP TABLE base_items;
ROLLBACK;
DROP VIEW dependent_view;
DROP TABLE base_items;
SELECT to_regclass('base_items'), to_regclass('dependent_view');
```
<!-- END SQLSTATE SNIPPET -->

The registry names are qualified by the runner inside a private schema. The final `to_regclass` check returns two nulls, proving that the intended objects were removed rather than silently cascading through an unknown graph.

The selected case observed `2BP01` on PostgreSQL 18.6 and 10.21. `DROP TABLE` reported the dependent view in DETAIL and suggested CASCADE; the explicit block was `INERROR`, `ROLLBACK` restored `IDLE`, and intentionally dropping the view first allowed the table to be removed.

## Versions {#versions}

The catalogue records this condition from the early historical boundary through the formal snapshots. The fixed 18.6 source includes dependency, shared-dependency, role, privilege, typed-table, and tablespace branches; the runtime case covers one ordinary view-to-table dependency on 18.6 and 10.21. Do not infer that a CASCADE hint is equally safe for all branches.

## Related {#related}

Compare [2B000 dependent privilege descriptors](../2b000/) and [42P01 undefined table](../42p01/) when a DROP or dependency report names the wrong layer.

## Sources {#sources}

- [`src.errcodes.18.6`](https://github.com/postgres/postgres/blob/724edf9bde9d356724ad384a2e196edc3c9f80f7/src/backend/utils/errcodes.txt) (SHA-256 `6e8de346643ba84aa3c9c6a73360acfc7b2dfb89162c06c08ce9bf5bcd5bbcba`)
- [`src.dependency.18.6`](https://github.com/postgres/postgres/blob/724edf9bde9d356724ad384a2e196edc3c9f80f7/src/backend/catalog/dependency.c#L1148-L1154) (SHA-256 `1878f848dae03e08424a47a09508f3443227ad67c4bc5e0aba3ec9655d015b74`)
- [`DROP TABLE documentation`](https://www.postgresql.org/docs/18/sql-droptable.html) · local call scan `src.calls.REL_18_6` (SHA-256 `9ee8a0e81d8f0825c5c1ae45583439859a26e602bdd4ce2f2a62aa278867ccbf`)
