Diagnostic Queries
Symptoms
CREATE INDEX CONCURRENTLY was issued inside a transaction block. Because it commits in multiple phases to avoid locking writes, it cannot run inside an outer transaction. PostgreSQL raises SQLSTATE 25001 (active_sql_transaction).
- Common in migration tools that wrap each step in a transaction.
- Applies to REINDEX CONCURRENTLY and DROP INDEX CONCURRENTLY too.
- Must be executed standalone with autocommit.
What the server log shows
ERROR: CREATE INDEX CONCURRENTLY cannot run inside a transaction block
Why PostgreSQL raises this — what the manual says
the CREATE INDEX reference (Building Indexes Concurrently):
“Another difference is that a regular CREATE INDEX command can be performed within a transaction block, but CREATE INDEX CONCURRENTLY cannot.”
The concurrent build commits between internal phases so it never holds a long write lock. Running inside an outer transaction would prevent those intermediate commits, so PostgreSQL rejects it with 25001.
Common causes
- A migration framework wrapping statements in a transaction by default.
- Issuing the command after
BEGIN. - Autocommit disabled on the connection.
How to fix it
- Run it outside a transaction (autocommit on).
- In migration tools, mark the step as non-transactional (e.g. disable the wrapping transaction for that migration).
- If a concurrent build fails midway, drop the leftover
INVALIDindex and retry.
Diagnostic query
-- Find invalid indexes left by a failed concurrent build
SELECT c.relname FROM pg_index i
JOIN pg_class c ON c.oid = i.indexrelid
WHERE NOT i.indisvalid;
Related & next steps
Reference: PostgreSQL 18 — CREATE INDEX.
Thanks — noted. This helps keep the database accurate.