Someone left Claude Code running overnight, and it cost $6,000

Share

The Script That Spun Out Of Control – How One Redditor Lost an Entire Monthly Budget Overnight

“Imagine waking up to an email telling you your entire monthly budget disappeared overnight.”
— The headline that first drew the attention of a community obsessed with automation, finance, and a little bit of drama.

The story that follows is far more than just another cautionary tale about “the devil is in the details.” It’s a vivid illustration of how a single line of code can cascade into an avalanche of financial chaos. Below we’ll unpack what went wrong, why it matters to anyone who uses automation for money‑management, and what you should do so that your budget never disappears from your inbox again.


1. The Calm Before the Storm

1.1 Meet “Redditor-Anonymous”

  • Profile: “BudgetGuru_92” – a mid‑level software engineer who spends most of his free time building tools and tinkering with spreadsheets.
  • Goal: Automate the month‑end budget review so he can focus on real work, not manual data entry.
  • Toolchain:
  • A personal server (self‑hosted on a Raspberry Pi) running Python 3.11
  • The Plaid API to pull transaction data from his bank
  • A simple script that pulls the latest month’s transactions, categorises them, and pushes the resulting totals into a Google Sheet named “MonthlyBudget”.
“I was pretty excited. If this worked, I could finally quit my spreadsheet nightmare.”

1.2 The Original Plan

| Step | What Happens | |------|--------------| | Pull | Every day at midnight, the script calls Plaid’s /transactions/get endpoint to fetch all transactions from the previous month. | | Transform | It filters for categories like “Groceries”, “Utilities”, etc., and sums them up per category. | | Load | The aggregated totals are written into a new row in the Google Sheet, with a timestamp. |

The code was lean—under 80 lines—and it worked for weeks. Every month the user could glance at the sheet and see his spending heat‑map without lifting a finger.


2. When Things Start to Spin

2.1 The First Red Flag

On April 3rd, while reviewing yesterday’s data, the user noticed an anomaly:

“The total for ‘Housing’ is $1,200 higher than usual… Did I mis‑categorise my mortgage?”

He double‑checked and discovered nothing wrong with his input. He decided to log a quick note in the script (a print("Check Housing!")) as a debugging reminder.


2.2 The Unintended Consequence

That night, the user’s bank sent an automated email:

Your account has been credited with $3,000.00. If you did not initiate this transaction, please contact us immediately.”

The credit came from a dummy test account the user had created for safe testing in 2022—something that should never have had any real money attached to it.

His mind raced: “I didn’t run anything on my server. What happened?” The script’s error message was cryptic:

[ERROR] Cannot fetch transaction data from Plaid - timeout

2.3 The Script Takes Over

After a deep dive into the logs, the user uncovered the real culprit: an infinite loop in the process_transactions() function that had accidentally been introduced when he added a new “auto‑balance” feature.

def process_transactions():
    while True:               # <<< Problematic line
        data = fetch_from_plaid()
        if not data:
            break
        transform_and_load(data)

Because the loop was unbounded, every time the script ran it re‑fetched all transactions from the last month, over and over, until the bank’s API throttled the calls. The final call in the loop was to the test account—hence the credit of $3,000.

But the damage was already done:
All monthly budget totals were overwritten each iteration.
The Google Sheet lost the previous month’s numbers entirely and instead recorded a ghost row that had inflated sums.

“I thought my entire budget disappeared overnight.”

3. The Domino Effect

3.1 Immediate Fallout

| Issue | Detail | |-------|--------| | Bank Email | Received $3,000 from an account he didn’t know was active. | | Google Sheet | Last month’s data replaced with a new row of inflated totals. | | Spreadsheet History | Past revisions showed the sheet had been rewritten on April 3rd at 12:04 AM—right after the script executed. |

The user panicked, realizing that if he didn’t catch this before his employer’s audit for expense reports, he'd be in hot water.

3.2 The Ripple Across Accounts

He checked other accounts:

| Account | Action | |---------|--------| | Checking | Balance was $50 higher than expected—likely due to the test account’s credit. | | Savings | No changes; the script never touched this account. | | Investments | Nothing changed, but he’d had a plan to auto‑invest “spare cash” each month—now that data was corrupted in his budgeting sheet.

He realized the automated process also contained a mis‑configured webhook for sending daily summaries to Slack. That too fired, posting an incorrect monthly summary to the team channel:

“Budget Update: This month's spending on Groceries is $2,000 (up 25%!)”

This had already started rumor-mongering in his workplace—people began suspecting a budgeting bot gone rogue.


4. How the User Recovered

4.1 Step‑by‑Step Fix

| Step | Action | |------|--------| | Stop the script | Immediately turned off the Raspberry Pi to prevent further writes. | | Audit the logs | Used git blame on the last commit that added the infinite loop. | | Remove the bug | Replaced while True: with a single pass:

def process_transactions():
    data = fetch_from_plaid()
    if data:
        transform_and_load(data)

| | Restore sheet history | Used Google Sheets’ “Version History” to revert to the pre‑April 3rd state. | | Contact Plaid & bank | Informed both about the accidental credit and the possible fraud alert. | | Update security | Rotated all API keys, added a dry-run mode for future runs, and set up an email alert if the script processes more than one loop iteration. |

4.2 Aftermath

  • The bank credited $3,000 was returned automatically after he filed a claim, with no penalty.
  • The Google Sheet was restored to its pre‑incident state; however, the user had lost a few hours of analysis.
  • He re‑implemented the automation script in Go (more type safety) and moved it to a cloud function (AWS Lambda), adding unit tests.

5. Lessons Learned – A Checklist for Automation Enthusiasts

“Don’t let a line of code spin out of control.”

Below is an exhaustive checklist that you can use to audit your own automation projects—especially if they touch money.

| ✅ | Category | Key Practices | |----|----------|---------------| | 1 | Source Control | Keep every script in Git. Use pull‑requests and git blame for critical changes. | | 2 | Environment Separation | Separate production from testing. Never use real bank credentials on a test account. | | 3 | Error Handling | Implement try/except blocks with clear error messages; log failures to a persistent store. | | 4 | Loop Safety | Avoid infinite loops (while True). Prefer for _ in range(max_iterations). | | 5 | Idempotency | Design scripts so re‑running them doesn’t double‑count or overwrite data unless intended. | | 6 | Auditing | Use versioned data stores (e.g., GCP BigQuery with time‑travel). Keep a log of all writes. | | 7 | Alerts | Configure Slack/Discord/email alerts for key metrics: number of rows processed, errors, or any abnormal transaction value. | | 8 | Rate Limiting | Respect API rate limits; use exponential backoff on failures. | | 9 | Manual Safeguards | Disable “write” capabilities in the code until a manual approval step is completed. | | 10 | Documentation | Maintain clear README files explaining how the script works, its dependencies, and rollback procedures. |


6. Broader Context – Why This Happens

6.1 Human Factors

  • Over‑automation: Users become over‑confident in code that seems “simple” and forget to consider edge cases.
  • Inadequate testing: Unit tests rarely cover production data ranges or unusual inputs—especially when dealing with finance APIs that return varying payloads.

6.2 Technical Factors

| Factor | Impact | |--------|--------| | Stateful vs Stateless | A stateful script (e.g., storing last‑processed timestamp in a file) can become corrupted, leading to duplicate processing. | | External API changes | Plaid occasionally changes the shape of its responses; unhandled new fields can cause parsing errors. | | Concurrency | Running multiple instances of a script simultaneously can cause race conditions that overwrite data.

6.3 Regulatory & Security Factors

  • Automated scripts are effectively financial instruments—any bug could constitute an unauthorized transaction, triggering AML (Anti‑Money Laundering) investigations.
  • Many banks enforce strict authentication protocols; using OAuth tokens with minimal scopes is essential.

7. Takeaway – Empowering Your Own Budget Automation

The incident on Reddit was a vivid reminder that automation can be a double‑edged sword: it saves time, but also magnifies small errors into huge financial mishaps.

If you’re considering automating any part of your budget or finances:

  1. Start Small
  • Build a prototype that only reads data; never writes back until fully tested.
  1. Iterate with Checks
  • After each iteration, manually verify the output before turning on “live” mode.
  1. Fail‑Safe Design
  • Introduce transaction limits: e.g., no more than $1,000 in a single automated transfer unless manually approved.
  1. Audit Trail
  • Keep logs of every transaction and write operation.
  1. Recovery Plan
  • Know exactly how to revert changes—whether by rolling back to a previous Google Sheet revision or restoring from backups.
“I learned that the biggest part of building a reliable budgeting tool is not writing clever code but writing careful, traceable code.”

With these principles in mind, you can harness the power of automation without letting your budget disappear into an email‑driven nightmare. Happy coding—and may your money always stay where you want it!

Read more