# 25001 — active_sql_transaction

> PostgreSQL SQLSTATE 25001: active_sql_transaction and the explicit transaction boundary required by utility commands.
---

# 25001 — active_sql_transaction

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

`25001` means the command requires a transaction boundary that the current session violates. The selected case runs `VACUUM` after `BEGIN`, receives the exact active-transaction error, rolls back, and then successfully runs `VACUUM` outside the transaction block.

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

| Field | Value |
| --- | --- |
| SQLSTATE | `25001` |
| Condition | `active_sql_transaction` |
| 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_ACTIVE_SQL_TRANSACTION` |
| Aliases | `—` |

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

## Meaning {#meaning}

PostgreSQL uses `25001` for several paths. `PreventInTransactionBlock` rejects a prohibited utility in an explicit transaction block, subtransaction, or function; the source formats these as `%s cannot run inside a transaction block`, `%s cannot run inside a subtransaction`, and `%s cannot be executed from a function`. Other paths reject creating a logical replication slot after transaction writes, exporting a snapshot from a subtransaction, or setting an imported snapshot after a query. `BEGIN` inside an already active block is a separate `WARNING`, so severity and recovery depend on the source path.

## Diagnosis {#diagnosis}

Record SQLSTATE, severity, primary message, transaction status, and the command. In the selected case the `VACUUM` ERROR moves the explicit session to `INERROR`; only `ROLLBACK` restores `IDLE`. For a function or subtransaction report, end that context and issue the command as one top-level utility statement outside any explicit transaction block (for example, a single command on an autocommit connection); retrying inside the same wrapper cannot satisfy `PreventInTransactionBlock`. For logical-slot and snapshot reports, inspect whether the transaction has writes, a subtransaction, or an earlier query. A `WARNING` that a transaction is already in progress is not the VACUUM ERROR and does not by itself require rollback. The selected repair then executes `VACUUM items` outside the transaction block and asserts success plus `IDLE`, proving the repair boundary instead of merely proving the connection is usable.

## Response {#response}

Move a prohibited utility outside the transaction block, function, or subtransaction that violates its contract: issue it as one top-level command outside any explicit transaction block, typically on an autocommit connection. After an ERROR in an explicit transaction, issue `ROLLBACK` before reuse; do not put the retry inside another `BEGIN`. A logical replication slot must be created in a transaction with no prior writes; export a snapshot outside a subtransaction, and call `SET TRANSACTION SNAPSHOT` before any query. Preserve an already active transaction after the `WARNING` branch instead of rolling it back solely because `BEGIN` was repeated. Follow the exact source path rather than applying the VACUUM repair mechanically.

## Observed diagnostics {#messages}

`18.6 (Homebrew) / latest`:SQLSTATE `25001`; primary `VACUUM cannot run inside a transaction block`; after_error `INERROR`; after_rollback `IDLE`; status_after_vacuum `IDLE`.
`10.21 (Debian 10.21-1.pgdg90+1) / pg10`:SQLSTATE `25001`; primary `VACUUM cannot run inside a transaction block`; after_error `INERROR`; after_rollback `IDLE`; status_after_vacuum `IDLE`.

## Source message templates {#source-templates}

The selected VACUUM run does not cover these source-backed branches:

- ERROR `%s cannot run inside a subtransaction` (the command name is substituted).
- ERROR `%s cannot be executed from a function` (the command name is substituted).
- ERROR `cannot create logical replication slot in transaction that has performed writes`.
- ERROR `cannot export a snapshot from a subtransaction`.
- ERROR `SET TRANSACTION SNAPSHOT must be called before any query`.
- WARNING `there is already a transaction in progress`.

These templates are source evidence, not additional runtime claims for this case.

## Representative case {#case}

In this example, `VACUUM` is rejected inside an explicit `BEGIN` block; after `ROLLBACK`, the same utility succeeds as a standalone command outside that block. The complete setup, assertions, and cleanup are in the [case export](../data/cases/25001.json):

<!-- BEGIN SQLSTATE SNIPPET: vacuum_inside_transaction -->
```sql
-- create
CREATE TABLE items(id integer PRIMARY KEY, note text NOT NULL);
-- seed
INSERT INTO items VALUES (1, 'seed');
-- begin
BEGIN;
-- trigger
VACUUM items;
-- rollback
ROLLBACK;
-- followup
SELECT 1 AS usable;
-- repair
VACUUM items;
```
<!-- END SQLSTATE SNIPPET -->

The SQLSTATE, diagnostic, transaction-state, and repair assertions for this excerpt are produced from the checked case registry; see the [structured evidence](../data/evidence/25001.json) and [case export](../data/cases/25001.json).

Authored evidence IDs: `identity`, `utility-path`, `other-paths`, `runtime`. Selected runtime records: `runtime.25001-batch1-latest2-20260909.latest`, `runtime.25001-batch1-pg10b-20260909.pg10`.

## Versions {#versions}

The locked catalogue observes the condition by `7.4` and in all listed formal snapshots. The selected VACUUM case passes on 18.6 and 10.21, including rollback recovery and the successful outside-transaction repair; other 25001 paths are source-backed only.

## Related {#related}

Compare [25000 invalid transaction state](../25000/), [25P02 failed SQL transaction](../25p02/), and [24000 invalid cursor state](../24000/).

## Sources {#sources}

- [`src.errcodes.18.6`](https://github.com/postgres/postgres/blob/724edf9bde9d356724ad384a2e196edc3c9f80f7/src/backend/utils/errcodes.txt) (SHA-256 `6e8de346643ba84aa3c9c6a73360acfc7b2dfb89162c06c08ce9bf5bcd5bbcba`)
- `src.calls.REL_18_6` (SHA-256 `9ee8a0e81d8f0825c5c1ae45583439859a26e602bdd4ce2f2a62aa278867ccbf`)
- [`src.xact.18.6`](https://github.com/postgres/postgres/blob/724edf9bde9d356724ad384a2e196edc3c9f80f7/src/backend/access/transam/xact.c#L3654-L3677) (SHA-256 `75b012c0b047d1dc905a30975c244beec366e45eac0dbf109fd21bcd611a8e39`)
- [`src.logical.18.6`](https://github.com/postgres/postgres/blob/724edf9bde9d356724ad384a2e196edc3c9f80f7/src/backend/replication/logical/logical.c#L444-L446) (SHA-256 `3f1bd4c3e627fe78522c4dc9bacf9fa8200e6c82c2d01f7670706eee102b76d1`)
- [`src.snapmgr-export.18.6`](https://github.com/postgres/postgres/blob/724edf9bde9d356724ad384a2e196edc3c9f80f7/src/backend/utils/time/snapmgr.c#L1152-L1154) (SHA-256 `b605b69e77a026c143f4cabca079de364b9a8732bfc40f7595be44fb0971ee8`)
- [`src.snapmgr-set-snapshot.18.6`](https://github.com/postgres/postgres/blob/724edf9bde9d356724ad384a2e196edc3c9f80f7/src/backend/utils/time/snapmgr.c#L1409-L1411) (SHA-256 `b605b69e77a026c143f4cabca079de364b9a8732bfc40f7595be44fb0971ee8`)
- [`doc.vacuum.18.6`](https://github.com/postgres/postgres/blob/724edf9bde9d356724ad384a2e196edc3c9f80f7/doc/src/sgml/ref/vacuum.sgml) (SHA-256 `80ca5592cda7b74938385f84f09faac33574374c1a05d605a2d55982e4cf1bbf`) · [official documentation](https://www.postgresql.org/docs/18/sql-vacuum.html)
