This was a bug in our own app. We found it in our own audit, not through a customer report, and it is fixed in Balanced 1.1.2. I am writing it up because the failure is easy to build into any statement parser, and easy to miss when you check a converted file.
The file
A plain CSV from a current account:
Example Bank Current Account Statement
Statement period: 01/03/2026 to 31/03/2026
Previous balance,1000.00
New balance,2720.00
Date,Description,Amount
02/03/2026,SALARY ACME LTD,2000.00
05/03/2026,PAYMENT TO LANDLORD,-800.00
13/03/2026,TESCO,-80.00
20/03/2026,PAYMENT TO HMRC,-100.00
28/03/2026,REFUND AMAZON,700.00
1000 + 2000 - 800 - 80 - 100 + 700 = 2720. It adds up.
Balanced showed a green "Balanced" status. But it had read the file as a credit card statement and flipped every sign: the salary came out as -2000, the refund as -700, the rent as +800. The only hint was a grey info line saying it was read as a card statement. The exports carried the flipped signs and negated balances.
Why the check passed
With every amount reversed and both balances negated, the arithmetic still closes. The flipped file starts at -1000, ends at -2720, and the rows in between add up to the difference. A reconciliation that only asks "do the opening balance, the rows and the closing balance agree?" cannot tell the two readings apart. A total can pass with every sign reversed.
Where the wrong decision came from
Two things combined:
- The labels "Previous balance" and "New balance" counted as a strong sign of a card statement. Banks print those labels on current accounts too.
- Once the file was treated as probably a card, the word "payment" in a description was used to settle it. Every "PAYMENT TO ..." line was negative, which looks like outgoing payments, and that was taken as proof that the signs were swapped.
The word "payment" is weak evidence. On a card statement it can be a payment you made to the card. On a bank statement it is a payment to someone else. It appears on both, so it cannot decide anything on its own. It fired on any bank statement with those two balance labels (or two other weak cues) whose only "payment" lines were outgoing. A second probe with "Statement balance" and "Previous balance" and lines like PAYMENT TO ELECTRIC CO gave the same flipped result.
What we changed in 1.1.2
The word "payment" no longer decides the sign. When the evidence is weak and the totals cannot settle it, Balanced now asks "bank account or card?" instead of picking one and showing green. If you converted a statement with "Previous balance" and "New balance" lines in 1.1.1, convert it again and check it. The change is listed in the Balanced changelog.
The general lesson for a parser: when the evidence is weak, ask. A confident green on a guess is worse than a question.
How to check your own file
- Pick two or three rows you know. Is your salary positive and your rent negative, or the other way round? Do this before you look at totals.
- Compare the opening and closing balance in the converted file with the PDF or CSV the bank gave you, including the sign of each.
- Check that money in and money out match the direction your bank shows, not just that the sum closes.
- If you wrote the parser, add a test where every sign and both balances are negated and make sure it is not accepted silently.
- Do not treat a single word such as "payment" or "credit" in a description as proof of the account type.
- Treat a green check as "the arithmetic agrees", not "the signs are right".
Balanced is the Mac app we make for converting bank statements and checking the result: kayabyte.dev/mac/balanced/. It runs on your Mac and needs macOS 15 or later.
AI-assisted: I used an AI tool for drafting help and checked the facts against our own audit notes and changelog.
Kayabyte
Top comments (0)