invalid transaction termination
A COMMIT or ROLLBACK command was issued in a context where transaction termination is not permitted, such as inside a PL/pgSQL function body or a trigger. Transaction control within functions is restricted to procedures called with CALL.
- 1COMMIT or ROLLBACK issued inside a PL/pgSQL function (not a procedure)
- 2A COMMIT inside a trigger function body
- 3A client-side driver issuing an autocommit COMMIT that conflicts with an open server-side transaction
- 4ROLLBACK TO SAVEPOINT issued when no matching savepoint exists
A PL/pgSQL function attempts to COMMIT inside its body.
CREATE OR REPLACE FUNCTION bad_commit() RETURNS void AS $
BEGIN
INSERT INTO log_table (msg) VALUES ('hello');
COMMIT; -- triggers 2D000
END;
$ LANGUAGE plpgsql;
SELECT bad_commit();expected output
ERROR: invalid transaction termination
Fix 1
Convert the function to a procedure (Postgres 11+)
WHEN When the function genuinely needs to commit mid-execution.
CREATE OR REPLACE PROCEDURE transactional_work()
LANGUAGE plpgsql AS $
BEGIN
INSERT INTO log_table (msg) VALUES ('step 1');
COMMIT;
INSERT INTO log_table (msg) VALUES ('step 2');
COMMIT;
END;
$;
CALL transactional_work();Why this works
In Postgres 11+, stored procedures (created with CREATE PROCEDURE and called with CALL) support transaction control (COMMIT and ROLLBACK) within their body. Functions called with SELECT do not, because functions participate in the calling query's transaction. Procedures have their own transaction management context.
Fix 2
Restructure to avoid mid-function commits
WHEN When running on Postgres 10 or earlier, or when a procedure conversion is not feasible.
-- Move all work into a single transaction managed by the caller:
BEGIN;
INSERT INTO log_table (msg) VALUES ('step 1');
INSERT INTO log_table (msg) VALUES ('step 2');
COMMIT;
-- Use SAVEPOINT for partial rollback within a transaction:
BEGIN;
SAVEPOINT sp1;
INSERT INTO log_table (msg) VALUES ('step 1');
SAVEPOINT sp2;
INSERT INTO log_table (msg) VALUES ('step 2');
-- If step 2 fails: ROLLBACK TO SAVEPOINT sp2;
COMMIT;Why this works
Transaction control belongs to the client layer. By managing COMMIT and ROLLBACK at the caller level and using SAVEPOINTs for partial rollback, the function body performs only data operations without needing transaction control commands, which are disallowed in function context.
✕ Use EXECUTE to dynamically run COMMIT inside a function
Dynamic EXECUTE of COMMIT still runs inside the function's transaction context and raises the same 2D000 error.
Stored procedures with CALL support transaction control (COMMIT/ROLLBACK). This was not available in functions in any version.
src/backend/tcop/utility.c — RequireTransactionBlock()
PL/pgSQL Transaction Management ↗CREATE PROCEDURE ↗Content generated with AI assistance and reviewed for accuracy. Found an error? hello@errcodes.dev