PostgreSQL『current transaction is aborted, commands ignored until end of transaction block』エラーの原因と対処

  • 作成日 2025.09.05
  • 更新日 2025.10.06
  • その他
PostgreSQL『current transaction is aborted, commands ignored until end of transaction block』エラーの原因と対処

トランザクション内で先行ステートメントが失敗すると、そのトランザクションは「失敗状態(aborted)」になり、ROLLBACK(またはSAVEPOINTによる部分ロールバック)で状態を戻すまで後続コマンドがすべて拒否される。代表メッセージ、発生条件、最短復旧、原因特定のコツ、psql/各言語ドライバでの実装例、再発防止策を体系化した。SQLSTATEは多くの元エラー(23505等)に続いて 25P02(in failed SQL transaction)として現れる。

エラーの意味と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)を両輪で用意