PostgreSQL『current transaction is aborted, commands ignored until end of transaction block』エラーの原因と対処
- 作成日 2025.09.05
- 更新日 2025.10.06
- その他
トランザクション内で先行ステートメントが失敗すると、そのトランザクションは「失敗状態(aborted)」になり、ROLLBACK(またはSAVEPOINTによる部分ロールバック)で状態を戻すまで後続コマンドがすべて拒否される。代表メッセージ、発生条件、最短復旧、原因特定のコツ、psql/各言語ドライバでの実装例、再発防止策を体系化した。SQLSTATEは多くの元エラー(23505等)に続いて 25P02(in failed SQL transaction)として現れる。
- 1. エラーの意味とSQLSTATE
- 2. 発生条件の早見表
- 3. 最短復旧(いますぐ直す手順)
- 4. まず原因を突き止める:最初に失敗したSQLを探す
- 5. 再現と症状(psqlの最小例)
- 6. オートコミットと暗黙トランザクションの基礎
- 7. SAVEPOINTで部分ロールバック(失敗を局所化)
- 8. psqlの便利設定(スクリプト運用向け)
- 9. アプリ別リカバリ例:Python(psycopg)
- 10. アプリ別リカバリ例:Java(JDBC)
- 11. アプリ別リカバリ例:Node.js(pg)
- 12. よくある元エラーと対処の要点
- 13. pg_stat_activityでセッション状態を可視化
- 14. UPSERTで「失敗→25P02」を回避
- 15. DDL/バッチの安全運用パターン
- 16. 再現→解消の通し例(SAVEPOINT活用)
- 17. チェックリスト(上から順に)
- 18. まとめ:運用の型
エラーの意味とSQLSTATE
・メッセージ:current transaction is aborted, commands ignored until end of transaction block
・状態:現在のトランザクションが失敗状態。COMMIT不可、実質的にROLLBACK一択
・典型表示(後続コマンドで繰り返し出る)
ERROR: current transaction is aborted, commands ignored until end of transaction block
・元の原因エラー(例):一意制約違反(23505)、外部キー違反(23503)、チェック制約違反(23514)、型変換エラー(22XXX)、タイムアウト(57014/55P03)など
発生条件の早見表
・BEGIN~COMMIT間で先行SQLがERRORになったのに、ROLLBACKやSAVEPOINTで回復せず後続SQLを実行した
・アプリ/ORMが自動でトランザクションを開始しており(暗黙Tx)、その最中に何らかのERRORが発生
・バッチでエラー検知せずに続行(例:psqlのスクリプトでON_ERROR_STOP未設定)
・例外:トランザクション外(オートコミット)では各SQLが独立し、このエラーは出ない
最短復旧(いますぐ直す手順)
-- 1) いったんトランザクションを捨てる
ROLLBACK;
-- 2) 必要ならやり直す(原因を除去してから)
BEGIN;
...(安全なSQL)...
COMMIT;まず原因を突き止める:最初に失敗したSQLを探す
・ログ/アプリ側の例外スタックで「最初にERRORになったSQL」を特定する
・psqlならエラー直後に詳細を再表示
\errverbose -- 直前エラーの詳細(psql)
\set VERBOSITY verbose -- 以降のエラーを詳細表示・サーバーログでは log_min_error_statement と log_line_prefix などを整えると特定しやすい
再現と症状(psqlの最小例)
DROP TABLE IF EXISTS u;
CREATE TABLE u(id int PRIMARY KEY, name text);
BEGIN;
INSERT INTO u VALUES (1,'ok');
INSERT INTO u VALUES (1,'dup'); -- ここで 23505 UNIQUE VIOLATION(失敗)
SELECT count(*) FROM u; -- ← 以降は 25P02: current transaction is aborted
ROLLBACK; -- これで解除オートコミットと暗黙トランザクションの基礎
・トランザクション外(オートコミット)では各SQLが独立:1文失敗しても次の文には波及しない
・BEGINを明示、またはドライバ/ORMが自動開始した境界内では1回の失敗で以降は全NG(25P02)
SAVEPOINTで部分ロールバック(失敗を局所化)
BEGIN;
SAVEPOINT sp1;
INSERT INTO u VALUES (1,'try'); -- 失敗があり得る操作
-- 失敗したら
ROLLBACK TO SAVEPOINT sp1; -- Tx全体は維持
-- 続行可能
INSERT INTO u VALUES (2,'ok');
COMMIT;・パターン:リスクの高い操作の直前にSAVEPOINT→失敗時は ROLLBACK TO SAVEPOINT
psqlの便利設定(スクリプト運用向け)
\set ON_ERROR_STOP on -- 最初のエラーでスクリプト中断(原因追跡向け)
\set ON_ERROR_ROLLBACK on -- 対話時、各文ごとにSAVEPOINTで自動復旧(BEGIN中)
\set VERBOSITY verbose
\errverboseアプリ別リカバリ例:Python(psycopg)
import psycopg
with psycopg.connect(dsn) as conn:
with conn.cursor() as cur:
try:
cur.execute("BEGIN")
cur.execute("INSERT INTO u VALUES (1,'x')")
cur.execute("INSERT INTO u VALUES (1,'dup')") # ここで例外
cur.execute("SELECT count(*) FROM u") # 到達不可:25P02
cur.execute("COMMIT")
except Exception:
cur.execute("ROLLBACK") # 失敗Txを即終了
# もしくは SAVEPOINT を使うなら:
cur.execute("BEGIN")
cur.execute("SAVEPOINT sp")
try:
cur.execute("INSERT INTO u VALUES (1,'dup')")
except Exception:
cur.execute("ROLLBACK TO SAVEPOINT sp")
cur.execute("COMMIT")アプリ別リカバリ例:Java(JDBC)
// 自動コミットがfalseなら、エラー後は必ず rollback() する
conn.setAutoCommit(false);
try (PreparedStatement ps = conn.prepareStatement("INSERT INTO u VALUES (?,?)")) {
ps.setInt(1, 1); ps.setString(2, "dup");
ps.executeUpdate(); // ここで例外→以後は25P02相当
conn.commit(); // 到達しない
} catch (SQLException e) {
conn.rollback(); // 直ちに失敗Txを終了
}
// SAVEPOINTパターン
conn.setAutoCommit(false);
Savepoint sp = conn.setSavepoint("sp1");
try {
// 危険操作
try (Statement st = conn.createStatement()) {
st.executeUpdate("INSERT INTO u VALUES (1,'dup')");
}
} catch (SQLException e) {
conn.rollback(sp); // 部分ロールバック
}
conn.commit();アプリ別リカバリ例:Node.js(pg)
const { Client } = require('pg');
const client = new Client({ connectionString: process.env.DSN });
await client.connect();
try {
await client.query('BEGIN');
await client.query('INSERT INTO u VALUES ($1,$2)', [1,'ok']);
await client.query('INSERT INTO u VALUES ($1,$2)', [1,'dup']); // ここで失敗
await client.query('COMMIT'); // 到達せず
} catch (e) {
await client.query('ROLLBACK'); // すぐ終わらせる
}
// SAVEPOINT例
await client.query('BEGIN');
await client.query('SAVEPOINT sp');
try {
await client.query('INSERT INTO u VALUES ($1,$2)', [1,'dup']);
} catch {
await client.query('ROLLBACK TO SAVEPOINT sp');
}
await client.query('COMMIT');よくある元エラーと対処の要点
・一意制約違反(23505):重複キーを事前に存在確認する、UPSERT(INSERT … ON CONFLICT … DO UPDATE/NOTHING)を使う
・外部キー違反(23503):親レコードの存在順序/削除ポリシーを見直す
・チェック制約(23514):入力値のバリデーション強化
・型変換エラー(22XXX):暗黙キャストやフォーマットを事前整形
・ロック/タイムアウト(55P03/57014):ロック順序の統一、タイムアウト値とリトライ戦略の導入
pg_stat_activityでセッション状態を可視化
SELECT pid, usename, state, query, xact_start
FROM pg_stat_activity
WHERE pid = pg_backend_pid();
-- state が 'idle in transaction (aborted)' なら、ROLLBACKが必要UPSERTで「失敗→25P02」を回避
-- 競合時に更新(または何もしない)でエラーを出さない
INSERT INTO u(id, name) VALUES (1,'alice')
ON CONFLICT (id) DO UPDATE SET name = EXCLUDED.name;
INSERT INTO u(id, name) VALUES (1,'bob')
ON CONFLICT (id) DO NOTHING;DDL/バッチの安全運用パターン
-- スクリプト先頭で安全網
\set ON_ERROR_STOP on
\set ON_ERROR_ROLLBACK on
\set VERBOSITY verbose
-- 冪等DDL(存在チェックで「失敗」を避ける)
CREATE TABLE IF NOT EXISTS u(id int PRIMARY KEY, name text);
ALTER TABLE u ADD COLUMN IF NOT EXISTS note text;再現→解消の通し例(SAVEPOINT活用)
DROP TABLE IF EXISTS u;
CREATE TABLE u(id int PRIMARY KEY, name text);
BEGIN;
INSERT INTO u VALUES (1,'ok');
SAVEPOINT sp; -- 危険区間の前
INSERT INTO u VALUES (1,'dup'); -- ここで失敗
-- ERROR発生後はTxがaborted、以降のコマンドは拒否される
-- ただし SAVEPOINT があるので:
ROLLBACK TO SAVEPOINT sp; -- 部分回復(Txは継続)
INSERT INTO u VALUES (2,'next'); -- 続行できる
COMMIT;チェックリスト(上から順に)
・最初に失敗したSQLをログ/例外で特定したか(\errverbose を活用)
・エラー後すぐに ROLLBACK または ROLLBACK TO SAVEPOINT を実行したか
・危険操作の直前に SAVEPOINT を置いているか
・一意/外部キー/チェック違反を事前に回避(UPSERT・バリデーション)
・スクリプトは ON_ERROR_STOP/ON_ERROR_ROLLBACK を設定したか
・アプリは例外発生時に確実に rollback() しているか
・長時間の「idle in transaction」を作らない(接続プール/タイムアウト設計)
まとめ:運用の型
・原因の一次エラーを必ず突き止める→失敗Txは即ROLLBACK→必要ならSAVEPOINTで局所化
・エラーを起こさない設計(UPSERT/事前検証/冪等DDL)と、起きた時に止血する実装(例外でrollback)を両輪で用意
-
前の記事
PostgreSQL「column reference is ambiguous」の原因と対処 2025.09.03
-
次の記事
PostgreSQL『syntax error in tsquery』の原因と対処(全文検索/tsquery) 2025.09.08
コメントを書く