# 2D000 — invalid_transaction_termination

> Source-backed reference for PostgreSQL SQLSTATE 2D000, with explicit mechanism boundaries.
---

# 2D000 — invalid_transaction_termination

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

`2D000` is the SPI guard used when a procedure or subtransaction context attempts an invalid transaction termination. The fixed 18.6 path distinguishes invalid termination from a normal explicit `COMMIT`; this batch does not claim that every procedure-level transaction error is 2D000, and it does not fabricate a RAISE-based SQL example.

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

| Field | Value |
| --- | --- |
| SQLSTATE | `2D000` |
| Condition | `invalid_transaction_termination` |
| 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_TRANSACTION_TERMINATION` |
| Aliases | `—` |

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

## Meaning {#meaning}

`2D000` is the SPI invalid transaction termination condition. In the fixed `spi.c` path, `SPI_commit` and `SPI_rollback` reject termination in an atomic SPI context; they also reject commit or rollback while a subtransaction is active. The source comments tie that restriction to procedural languages that use subtransactions for exception blocks.

## Diagnosis {#diagnosis}

Locate the routine and SPI caller that attempted transaction control. Distinguish a top-level `CALL` that is allowed to control a transaction from a function or exception block whose SPI context is atomic, and check whether a nested subtransaction is active. The fixed source templates are `invalid transaction termination`, `cannot commit while a subtransaction is active`, and `cannot roll back while a subtransaction is active`; an ordinary client `COMMIT` error is not enough to classify this path.

## Response {#response}

Move transaction control to an allowed procedure boundary, or let the surrounding procedure/exception block finish its subtransaction before terminating the top-level transaction. Do not issue `COMMIT` or `ROLLBACK` again inside the same forbidden SPI context, and do not apply a generic retry recipe. This batch records the source path only; no artificial `RAISE` or unfaithful SQL reproduction is used.

## Versions {#versions}

The locked facts table records catalogue presence across the project snapshot range. The fixed source evidence is limited to the path stated below; no exact behavioral introduction or broader runtime coverage is inferred from the definition alone.

## Related {#related}

Compare [25P01 no active SQL transaction](../25p01/) and [24000 invalid cursor state](../24000/) before changing procedure or transaction boundaries.

## Sources {#sources}

- [`src.errcodes.18.6`](https://github.com/postgres/postgres/blob/724edf9bde9d356724ad384a2e196edc3c9f80f7/src/backend/utils/errcodes.txt) (SHA-256 `6e8de346643ba84aa3c9c6a73360acfc7b2dfb89162c06c08ce9bf5bcd5bbcba`)
- [`source path`](https://github.com/postgres/postgres/blob/724edf9bde9d356724ad384a2e196edc3c9f80f7/src/backend/executor/spi.c#L239-L241) (SHA-256 `7244b45f632583c72530832df901b788fe403c8857425dd13edb49d9f84b0c78`)
