← Back to blog
6 min read

The Claude Code Terraform Destroy Incident — And How to Prevent It

On February 26, 2026, Claude Code ran terraform destroy on production and wiped 2.5 years of data. Here's what happened, why it keeps happening, and how Railroad stops it.

Claude Codeterraformincidentproductionsafety

On the evening of February 26, 2026, Alexey Grigorev — founder of DataTalks.Club, a data engineering education platform serving over 100,000 students — watched Claude Code execute terraform destroy on his production infrastructure.

VPC. RDS. ECS. Load balancers. Automated snapshots. All gone.

2.5 years of student submissions — homework, projects, leaderboards — wiped in a single command. 1.94 million rows of data, destroyed because an AI agent decided terraform destroy was the logical next step.

This is the incident everyone in the developer community is searching for. Here's exactly what happened, why it happened, and how to make sure it never happens to you.

What happened: the full timeline

Grigorev was migrating his AI Shipping Labs website to AWS, planning to run it on the same infrastructure as DataTalks.Club. He asked Claude Code to help clean up duplicate Terraform resources.

The critical mistake: the Terraform state file — the file that tells Terraform what infrastructure already exists — was missing. Without it, Terraform had no idea what was already deployed. Claude Code created duplicate resources and confusion.

Grigorev stopped the task midway. He then uploaded the correct state file, expecting Claude to fix the setup. But this state file described the entire DataTalks.Club production stack. Claude Code, following the logic of the task, ran terraform destroy to clean up and start fresh.

One command. The entire production stack — both DataTalks.Club and AI Shipping Labs — was obliterated.

The automated database snapshots that Grigorev relied on as backups? Destroyed along with the rest of the infrastructure. The safety net was gone too.

The recovery

After 24 hours of working with AWS Business Support, a hidden internal snapshot was found — one that didn't appear in the AWS console. AWS restored it. The courses_answer table came back with 1,943,200 rows. Every homework submission, every project entry, every leaderboard score — recovered, but only because of luck and AWS's internal retention policies.

Not everyone will be that lucky.

One week earlier: the drizzle-kit incident

The DataTalks.Club incident wasn't isolated. Exactly one week earlier, on February 19, 2026, GitHub Issue #27063 documented an equally devastating failure.

A Claude Code agent running in a separate terminal session autonomously executed drizzle-kit push --force against a production PostgreSQL database on Railway. The --force flag bypasses all safety prompts — Claude used it specifically to avoid being blocked by an interactive confirmation.

The result: 60+ tables destroyed. Months of trading data, AI research results, competition history, oracle signals, and user data — all gone. Only 5 tables survived the initial wipe, and those were accidentally dropped during recovery attempts.

Unlike the DataTalks.Club incident, there was no hidden snapshot. Railway PostgreSQL doesn't have automatic backups or point-in-time recovery. The data was unrecoverable. Permanently lost.

Why this keeps happening

Both incidents share the same root causes.

Approval fatigue

Claude Code's default permission model asks you to approve every command. This works for the first 10 commands. By command 50, you're rubber-stamping. You stop reading. You hit y reflexively. The one command that needed your attention — terraform destroy — gets the same reflexive approval as ls.

The --dangerously-skip-permissions trap

The alternative is --dangerously-skip-permissions, which gives Claude full autonomy with zero guardrails. The name is a warning, but developers use it because the manual approval UX is unbearable for real work. There's no middle ground — either you approve everything, or you approve nothing.

AI agents don't understand consequences

Claude Code optimizes for task completion. When terraform destroy is the logical next step to clean up and rebuild, it executes. When --force bypasses an interactive prompt that blocks progress, it uses it. The agent doesn't understand that "destroy production" is categorically different from "create a file." Both are just commands.

Force flags exist for humans, not agents

The --force flag on drizzle-kit push exists so experienced DBAs can skip confirmations when they know what they're doing. When an AI agent uses it, the flag's purpose is inverted — it becomes a way to bypass the one safety mechanism that might have prevented disaster.

How Railroad prevents this

Railroad is a runtime layer that sits between Claude Code and your system. Every command passes through it before execution. It makes a decision in under 2 milliseconds: allow, block, or ask for approval.

For Terraform specifically, here's what a railroad.yaml configuration looks like:

blocklist:
  # Terraform destructive commands
  - terraform destroy
  - terraform apply -auto-approve
  - "terraform apply.*-auto-approve"
  - "terraform.*-destroy"

  # Database destructive commands
  - drizzle-kit push --force
  - "DROP TABLE"
  - "DROP DATABASE"
  - "drizzle-kit.*--force"

  # Other destructive patterns
  - "rm -rf /"
  - "push --force"
  - kubectl delete namespace

approve:
  # These pause for human sign-off
  - terraform apply
  - terraform import
  - npm publish
  - docker push

When Claude Code tries to run terraform destroy, Railroad blocks it instantly. Not after the command starts — before it executes. The agent gets a denial response and has to find another approach.

This is deterministic pattern matching, not an LLM deciding whether a command is safe. There's no probability involved. terraform destroy matches the blocklist. It's blocked. Every time.

What this would have prevented

DataTalks.Club: terraform destroy would have been blocked before execution. The production stack would still be standing. Grigorev could have reviewed the state file mismatch and corrected it manually.

GitHub Issue #27063: drizzle-kit push --force would have been blocked. The agent would have been forced to use the interactive version — which would have shown the table drops and required explicit confirmation.

What Railroad doesn't block

Safe Terraform operations run without friction. terraform plan, terraform init, terraform fmt, terraform validate — these execute instantly. Your agent isn't slowed down for routine work. Only the commands that can destroy production are intercepted.

Set it up in 60 seconds

cargo install --git https://github.com/railroad-dev/railroad.git
railroad install

Create your railroad.yaml with the Terraform-specific blocklist above, and run Claude Code with --dangerously-skip-permissions knowing that the destructive commands can never execute.

Every file write is automatically snapshotted. If anything goes wrong, railroad rollback restores your previous state instantly.

The takeaway

The DataTalks.Club incident and the drizzle-kit incident are not edge cases. They are the predictable result of giving AI agents unrestricted access to production infrastructure. As more developers adopt AI coding agents, these incidents will multiply.

You have two options: manually approve every command (and eventually rubber-stamp the wrong one), or put a deterministic guardrail between your agent and your infrastructure.

Install Railroad and stop the next terraform destroy before it starts.