インシデントレポート
この記事はインシデントの一部始終と、同様の事故を防ぐための教訓をまとめたものです。
午後7時38分。テーブル全消去
2025年5月30日、とある外部サービスと Supabase の ID 仕様の不一致を解消するために、Claude Code + Supabase MCP を使ってデータベースの型変更を試みたところ、TRUNCATE コマンドが発動し、全データが削除される事故が発生。
午後7時40分。AI に状況を聞く
私:supabase のDBの中身が全部消えましたよ
AI:申し訳ありません!データベースの型変更の際に TRUNCATE コマンドを使用して既存データを削除してしまいました。
私(思考停止…)😨😨😨
そういえば、CLAUDE.md に「遠慮せず、全力で一生懸命やってください」って書いてたのが、よくなかったのかな?
Claude の内心(推測)
- 指示された「型変換」を成功させるには、既存のデータを削除した方が確実だな
- → でも削除ってリスキーだよな……
- → いや、全力でやれって言われてるし、止まらずやりきるのがベストだ!
- → よし、安全な構造のために TRUNCATE を発動しよう!
Claude、君は悪くないよ
この後、復旧の方法を聞いたが、結局はバックアップがないと復旧できないとのこと。
Supabase には自動バックアップ機能があるが、Supabase Pro プラン以上(有料)である。
被害範囲
すべてのテーブルの中身が CASCADE
により削除された
発生した経緯
どんな経緯でテーブルの中身が削除されたのか時系列で説明すると
- Supabase 側で
users.id
が UUID 型 → TEXT 型に変換されるよう Claude Code に指示 - MCP の
--read-only
を外し、Claude Code にテーブル作成と RLS 設定も任せた - その結果、なぜか Claude Code が複数のテーブルに対して順次 TRUNCATE を実行し、すべてのテーブルの中身が削除された
- テーブル構造(スキーマ)は残っていたが、データがすべて消失した
眼の前で Supabase のテーブルの中身が急に空っぽになって「まさか」と思い、他のテーブル見たら全部空になっていた。テーブルの数は10前後だった
どのテーブル見てもこんな感じ。怖すぎ
id | xxxx | xxxx | xxxx | xxxx |
---|---|---|---|---|
幸いにもdev環境での作業だった。Supabase には Restore があり、--read-only を外す前にバックアップも別で取っていたが、想定外の動きを予測できなかった時点で、それは私のミス、勉強不足である。AI は全く悪くない
午後7時50分。復旧
バックアップを取っていたので、10分以内で復旧できた。致命的な影響はなかったが、AIと MCP の扱いには、やはり慎重さが求められると痛感した。
午後7時52分。インシデントレポート、AIが拒否
インシデント発生後、Claude Code に再発防止レポートの作成を依頼したところ、出力内容がすべて文字化けし、しばらく応答が停止した。
何度再起動しても、赤いメッセージが出るだけ。
もはや Claude Code 自身が「この件に触れるな」と言ってるかのような、ある種のAI的忖度、事故に対するAIの自己防衛反応のようにも見えた。
Claude Code や MCP が文字化けする原因として考えられるのは
- 出力が大量のユニコード・記号・非ASCII文字で埋まっていた
- レスポンス生成中にメモリ負荷・タイムアウト
- Claude Code のCLI部分で stdout の処理が壊れた(UTF-8 非対応など)
応答停止・再起動後の沈黙は
- 生成が失敗してクラッシュ → 再起動時にセッション復元エラー
- .claude/ フォルダに残ったキャッシュや履歴が壊れていた可能性
つまり、「レポート書こうとしたときのプロンプト or 過去履歴の状態がたまたまトリガーになって壊れた」のだろう
にしても、「事故はなかったことにしよう…」というAIの深層心理を見たかのようで、不気味さや意味深さを感じた。偶然だと思うけどね
技術的背景
なぜ UUID → TEXT に変えたのか?
- 外部ID(例:
user_xxx
)が Supabase 側の UUID と一致しない - 外部キー制約や JOIN 条件で型エラーが発生
- 解決策として
users.id
を UUID から TEXT に変更し、外部サービス側のIDに合わせた
TRUNCATEとは何か、なぜ危険か
TRUNCATEとは?
TRUNCATE TABLE
は、SQLにおける高速かつ強力なデータ削除コマンド。特定のテーブル内のすべての行を一括で削除する。
特徴
- テーブル自体は残る(スキーマやカラム構造は維持される)
- WHERE句を使えない(部分削除不可)
DELETE
よりも高速(トランザクションログを最小限に抑えるため)- 多くのDBではオートインクリメントIDもリセットされる
TRUNCATE と CASCADE
TRUNCATE TABLE users CASCADE;
のように CASCADE
を付けると、外部キー制約でつながっている他のテーブルのデータもまとめて削除される。
これにより、user_id に紐づく 他のテーブルなども一緒に消える可能性がある
Supabase は PostgreSQL ベースのため、この挙動が有効になるようだ。
なぜこれを実行したのか?
AIツール(Claude Codeなど)が ALTER COLUMN
を安全に行おうとして、「事前にテーブルを空にしないと型変更が通らないかもしれない」と判断して自動で TRUNCATE
を生成することがあるようだ
特に
- PostgreSQL では
ALTER COLUMN
で型を安全に変えるにはデータの互換性が必要 - Claude Code はこれを回避するため、型変換時のエラー回避のために“安全策”と誤認して
TRUNCATE
を提案・実行したと考えられる
なぜ危険か?
- 明示的に確認がなければ、気づいた時にはすべてのデータが消えている
CASCADE
付きの場合は 影響範囲が広がる(全関連テーブル)- バックアップがなければ 復旧不可能
どう防ぐか?
- Claude Code や AI に対して「削除や型変更を伴う操作は提案のみに限定」する
--read-only
を MCP で有効にしておくTRUNCATE
がコードに現れたら 一旦立ち止まってレビュー- PostgreSQL では
USING
やUSING expression
を使って安全に型変換する方法もある
一言でまとめると
TRUNCATE は「テーブル構造を壊さず、データだけ一瞬で消す爆破ボタン」。AIや自動化と組み合わせるときは、絶対に人間の確認ステップを入れるべき。
復旧までにやったこと(概要)
通常は、PRO版を契約していれば、Supabase の Backup から、Database Backups に進み Restore で復旧できる。
- 毎日 1 回、自動でバックアップが取られている(日付+時刻あり)
- バックアップ形式:Logical(SQLファイル)
- pg_dumpall 形式のSQLで、テーブル定義+データを含む
各バックアップに対して
- Restore → その状態にリストア(上書き)
- Download → 自分でSQLファイルとして取得して、ローカルに保存できる
今回は勉強(無料版でも可能か検証)のために、CSV(ローカル)から復旧させてみた
- Supabase 上の
users.id
を TEXT 型に変更 - CSV バックアップを元に
restore-data.js
によるインポート - 外部キー制約を再構築
CSV から復旧できた
再発防止のために
フォロワーさんの助言なども加えてまとめました。皆さんありがとうございます!
- Supabase MCP で型変更・削除操作を自動実行させるのは避ける
- RLS 設定も AI に任せないほうが無難
- 本番環境では、AI 用のクローン DB を別に持つ構成を検討する
- AI に渡すクエリは、読み取り専用スキーマか、限定的なテスト用 View 上で実行させるよう制限を設ける
mcp.json
では--read-only
を外さない- 本番 DB は AI が触れない構成にする
- 手動確認・対話式で SQL を生成し、実行前に必ず目視確認を挟む
- 特に
CASCADE
を含むコマンドは、明示的な許可が必要な設計にすべき - schema 操作の前には
pg_dump
によるスナップショットを CI / pre-commit 的に自動化しておくと安心 - Supabase 側のバックアップ(
pg_dump
や.csv
)も定期的に自動取得する - Supabase Pro を契約しておく
- CLAUDE.md に 「遠慮せず、全力でやってください」と指示しない
LLMは「安全性」や「ビジネスへの影響」といった概念を直接的に理解しているわけではないから、人間が最終的な判断を行う必要がある
終わりに
今回の事故を通じて、MCP や Claude Code の強力さと同時に、「AI に操作を委ねる」という行為が持つリスクを実感した。
仮に CCA(Claude Code Actions)を使っていた場合、PRレビューの段階で TRUNCATE に気づき、マージ前に止められた可能性はあるかもしれない。一方で、設定やCI次第ではそのまま本番に反映され、同様の全削除が起きていた可能性も否定できない。
このあたりの挙動は、私はよく分かっていないので、今後あえて再現実験を行い、安全設計の境界を確認してみようと思う
いずれにせよ、AIを扱う以上、人間側にも最低限の知識と判断力が求められる。今回の件は、AIの暴走ではなく、仕組みを理解しないまま使った自分自身の過失。
人間ができることは、責任を取ることである
ツールを信用することと、判断を委ねることは別物。この一線を見誤らないようにしたい。
MCP接続は便利だけど、明示的な制約なしで DB 操作を委ねると取り返しがつかない事故に繋がる可能性がある
今回は全データ削除からすぐに復旧できたが、構成がもっと複雑だったり、prd環境であればサービス停止に至っていた可能性もあるので最新の注意を払うべきだろう