You cannot use ROLLBACK in PL/pgSQL, except in certain limited cases inside procedures.

You don't need to explicitly roll back in your PL/pgSQL code. Just let the exception propagate out of the PL/pgSQL code, and it will cause an error, which will cause the whole transaction to be rolled back.

Your comments suggest that this code is called from an SQL script. Then the solution would be to have a COMMIT in that SQL script at some place after the PL/pgSQL code. That would end the transaction and roll it back.

Answer from Laurenz Albe on Stack Overflow
๐ŸŒ
Reddit
reddit.com โ€บ r/postgresql โ€บ rolling back transaction when transaction errors
r/PostgreSQL on Reddit: Rolling back transaction when transaction errors
March 9, 2023 -

I am newer to Postgres... and I don't understand a situation I ran into today.

I built a transaction query and when testing my backend, I passed in the wrong userid. The transaction failed and never reached the COMMIT statement. But it was in a failed state and hung.

My query was wrapped in a try/catch (using typescript) but no error was thrown. And this blocks the other queries from running (which makes sense).

Even if an error was thrown...

How do you rollback a failed transaction?

For whatever reason I am not understanding how to "close out" a failed transaction. I was under the impression that if a transaction fails... Postgres commits nothing and all queries are "thrown out" so to speak.

Thanks for the help understanding this.

Discussions

Is ROLLBACK; on error automatic in a basic PostgreSQL DDL script (BEGIN; <STATEMENTS;> COMMIT;)? - Database Administrators Stack Exchange
I want to run numerous statements in an atomic transaction on a Postgres database. Is it sufficient to put BEGIN; at the start of the script and COMMIT; at the end of the script? I want to ensure t... More on dba.stackexchange.com
๐ŸŒ dba.stackexchange.com
postgresql - If I actively BEGIN a transaction, but fail to ROLLBACK at error, will PG become confused? - Database Administrators Stack Exchange
The transaction won't be "auto rolled back", it will be marked as failed, and the only option you have, is to do a rollback. If you did not run rollback in the function but simply return from it in case of an error, then the UPDATE following the function call would result in "current transaction ... More on dba.stackexchange.com
๐ŸŒ dba.stackexchange.com
postgresql - Catching exceptions and rolling back transactions - Database Administrators Stack Exchange
Read the manual about Transaction Management. Be sure to know the difference between functions and procedures in Postgres: In PostgreSQL, what is the difference between a "Stored Procedure" and other types of functions? When to use stored procedure / user-defined function? ... Since this in now a Hot Network Question, it should be noted that this does not always apply outside of Postgres. For example, SQL Server's approach to deciding if an error should rollback ... More on dba.stackexchange.com
๐ŸŒ dba.stackexchange.com
February 29, 2024
database - Is it required to execute 'ROLLBACK' upon error? - Stack Overflow
Show activity on this post. I'm a newbie with PostgreSQL/libpq. So please help me clarify my confusion: Assuming I start by executing a 'START TRANSACTION' and do proper error checking (PQresultStatus(res) != [proper_success_value]), am I required to execute a 'ROLLBACK' if something goes wrong ... More on stackoverflow.com
๐ŸŒ stackoverflow.com
June 26, 2017
๐ŸŒ
DEV Community
dev.to โ€บ aws-heroes โ€บ postgresql-rollback-on-error-515a
PostgreSQL aborts the transactions on error - DEV Community
April 28, 2025 - In interactive usage with PSQL, setting ON_ERROR_ROLLBACK is advisable to prevent rolling back all previous work due to a simple typo causing an error. While it is unnecessary if you do not start a transaction explicitly, and rely on autocommit, ...
๐ŸŒ
GitHub
github.com โ€บ acakojic โ€บ postgresql-learning โ€บ blob โ€บ main โ€บ transactions โ€บ 1_4_postgresql_rollback_in_transaction.md
postgresql-learning/transactions/1_4_postgresql_rollback_in_transaction.md at main ยท acakojic/postgresql-learning
If an error occurs at any point in the transaction, you can use ROLLBACK to undo any changes made so far, essentially reverting the database to its state before the transaction began.
Author ย  acakojic
๐ŸŒ
End Point Dev
endpointdev.com โ€บ blog โ€บ 2015 โ€บ 02 โ€บ postgres-onerrorrollback-explained
Postgres ON_ERROR_ROLLBACK explained | End Point Dev
Way back in 2005 I added the ON_ERROR_ROLLBACK feature to psql, the Postgres command line client. When enabled, any errors cause an immediate rollback to just before the previous command. What this means is that you can stay inside your transaction, even if you make a typo (the main error-causing ...
๐ŸŒ
PostgreSQL
postgresql.org โ€บ docs โ€บ 9.3 โ€บ tutorial-transactions.html
PostgreSQL: Documentation: 9.3: Transactions
November 2, 2020 - Moreover, ROLLBACK TO is the only way to regain control of a transaction block that was put in aborted state by the system due to an error, short of rolling it back completely and starting again.
Find elsewhere
๐ŸŒ
GeeksforGeeks
geeksforgeeks.org โ€บ postgresql โ€บ postgresql-rollback
PostgreSQL - ROLLBACK - GeeksforGeeks
July 23, 2025 - PostgreSQL ROLLBACK command is to undo changes made during a transaction. If a transaction encounters an error or needs to be reverted for any reason, ROLLBACK can restore the database to its state before the transaction began.
๐ŸŒ
PostgreSQL
postgresql.org โ€บ docs โ€บ current โ€บ sql-rollback.html
ROLLBACK - PostgreSQL: Documentation: 18
May 14, 2026 - If AND CHAIN is specified, a new ... finished one. Otherwise, no new transaction is started. Use COMMIT to successfully terminate a transaction. Issuing ROLLBACK outside of a transaction block emits a warning and otherwise has no effect. ROLLBACK AND CHAIN outside of a transaction block is an error...
๐ŸŒ
PostgreSQL
postgresql.org โ€บ about โ€บ news โ€บ server-side-rollback-at-statement-level-for-postgresql-2102
PostgreSQL: Server side rollback at statement level for PostgreSQL
November 2, 2020 - In PostgreSQL the transaction cannot continue when you encounter an error and the entire work done in the transaction is rolled back. Oracle or DB2 have implicit savepoint before each statement execution which allow a rollback to the state just before the statement failure.
๐ŸŒ
Postgres Professional
postgrespro.com โ€บ list โ€บ thread-id โ€บ 2089885
Thread: how to continue a transaction after an error? : Postgres Professional
> Not possible; the error handling in PGSQL is a bit of a mess (not necessarily a fault of PG), and it's not possible (currently) to rollback single statements inside a larger transaction.
๐ŸŒ
EnterpriseDB
enterprisedb.com โ€บ postgres-tutorials โ€บ how-work-postgresql-transactions
How to work with PostgreSQL transactions | EDB
Recover from user error: We can ROLLBACK (erase) a mistake. For instance, I want to delete a particular row from a table. I fired a DELETE statement without a WHERE condition (by mistake). Unfortunately that would delete all records from my table, but I just wanted to delete only a few rows. However, I can easily recover from my mistake if I am inside a transaction block โ€” by firing the ROLLBACK command.
๐ŸŒ
Medium
franckpachot.medium.com โ€บ postgresql-subtransactions-savepoints-and-exception-blocks-67e0fbd412af
PostgreSQL subtransactions, savepoints, and exception blocks | by Franck Pachot | Medium
October 20, 2019 - Basically, in Oracle, the call to the stored procedure follows statement-level atomicity where an internal savepoint is set before any statement (SQL or PL/SQL) and an unhandled exception (including a re-raise exception) rolls back to it. Thatโ€™s different in PostgreSQL where no savepoint is set implicitly, and the session has to rollback the whole transaction when an error occurs. The savepoint set before the PL/pgSQL block is only to rollback changes before executing the exception block.
๐ŸŒ
PostgreSQL
postgresql.org โ€บ docs โ€บ current โ€บ tutorial-transactions.html
PostgreSQL: Documentation: 18: 3.4. Transactions
May 14, 2026 - Moreover, ROLLBACK TO is the only way to regain control of a transaction block that was put in aborted state by the system due to an error, short of rolling it back completely and starting again.
Top answer
1 of 1
9

The problem I have with your example is that you're talking about JDBC behaviour, but also using explicit "start transaction" etc commands, which seems a bit of a clash, since I'd expect you'd use JDBC's auto-commit mode to manage transactions.

If you are in auto-commit mode, then the two inserts will each be in their own transaction, and the throw of a SQLException for the first one will not affect the second.

If you are not in auto-commit mode, then an implicit "start transaction" is generated before the first insert, and the second insert cannot be processed until the transaction is rolled back. This behaviour is quite different from if you were executing the script with psql.

(JDBC does not specify whether drivers/connections should default to auto-commit on or off, you should always explicitly set it)

Postgresql treats any error processing a statement as immediately aborting the transaction-- essentially like the XACT_ABORT mode in SQL Server. The intent being that if you submit a sequence of commands as a transaction, each one is dependent on the previous ones, so the failure of any one invalidates all the subsequent ones.

If this isn't the behaviour you want inside a transaction, you need to surround the potentially-aborting updates with creating a savepoint, and rolling back to that savepoint in case of an error.

Beware of looking at very old discussions of behaviour (bugs over ten years old definitely count), as at some point in Postgresql's history, there was a session variable called autocommit, and the behaviour could have been quite different. That variable is gone now, replaced (as I understand it) with the concepts of the database or the JDBC driver automatically wrapping commands inside transactions (so in fact there is not really any such thing as non-transactional interaction with postgresql).

Here is what happens when you execute the script you suggest with psql:

steve@steve@[local] =# start transaction;
START TRANSACTION
steve@steve@[local] *=# create table test(id int primary key);
NOTICE:  CREATE TABLE / PRIMARY KEY will create implicit index "test_pkey" for table "test"
CREATE TABLE
steve@steve@[local] *=# insert into test values (1);
INSERT 0 1
steve@steve@[local] *=# commit;
COMMIT
steve@steve@[local] =# 
steve@steve@[local] =# -- Following statement throws a SQLException(duplicate key) in
steve@steve@[local] =# -- PG, SS and ORacle
steve@steve@[local] =# insert into test values (1);
ERROR:  duplicate key value violates unique constraint "test_pkey"
DETAIL:  Key (id)=(1) already exists.
steve@steve@[local] =# 
steve@steve@[local] =# -- Following statement behaves differently for different DBMS:
steve@steve@[local] =# -- SS and OR: No error...statement runs fine
steve@steve@[local] =# -- PG: Another SQLException thrown...must rollback or commit
steve@steve@[local] =# insert into test values (99);
INSERT 0 1

In order to get the same behaviour as you wrote in the script, you'd have to turn off auto-commit before doing the insert- that stops the JDBC driver from issuing an implicit "start transaction" before it executes the next statement. If you put that implicitly-generated transaction into the psql script, it produces the error you describe:

steve@steve@[local] =# start transaction; -- generated by JDBC driver
START TRANSACTION
steve@steve@[local] *=# -- Following statement throws a SQLException(duplicate key) in
steve@steve@[local] *=# -- PG, SS and ORacle
steve@steve@[local] *=# insert into test values (1);
ERROR:  duplicate key value violates unique constraint "test_pkey"
DETAIL:  Key (id)=(1) already exists.
steve@steve@[local] !=# 
steve@steve@[local] !=# -- Following statement behaves differently for different DBMS:
steve@steve@[local] !=# -- SS and OR: No error...statement runs fine
steve@steve@[local] !=# -- PG: Another SQLException thrown...must rollback or commit
steve@steve@[local] !=# insert into test values (99);
ERROR:  current transaction is aborted, commands ignored until end of transaction block

As an illustration of why this behaviour exists, consider what happens if I run the first transaction again. The intent is "create the table and populate it with a single row":

steve@steve@[local] =# start transaction;
START TRANSACTION
steve@steve@[local] *=# create table test(id int primary key);
ERROR:  relation "test" already exists
steve@steve@[local] !=# insert into test values (1);
ERROR:  current transaction is aborted, commands ignored until end of transaction block
steve@steve@[local] !=# commit;
ROLLBACK

So as soon as a problem is detected ("test" already exists), the remaining data manipulation isn't appropriate (the row already existed too, anyway)

๐ŸŒ
W3Resource
w3resource.com โ€บ postgresql-exercises โ€บ postgresql-query-to-update-multiple-records-within-a-transaction-and-then-rollback-if-an-error-is-detected.php
PostgreSQL - Transaction Rollback for Error Handling
October 29, 2025 - ROLLBACK : Reverts all changes made in the transaction. ... Useful in scenarios where an unexpected error occurs and changes must be undone immediately. ... In a real application, the rollback would be triggered by error detection rather than ...