Report #30794
[bug\_fix] ERROR: could not serialize access due to concurrent update \(40001\)
Implement application-level retry logic with exponential backoff for transactions that fail with error code 40001. Alternatively, use \`SELECT FOR UPDATE\` to acquire an exclusive lock on the rows before reading them, preventing concurrent updates. For idempotent operations, use \`UPDATE ... RETURNING\` in a single statement to avoid the read-modify-write cycle. Root cause: In \`REPEATABLE READ\` or \`SERIALIZABLE\` isolation levels, Postgres uses snapshot isolation. If two transactions read the same row and then both try to update it, the first to commit wins; the second receives a 40001 error on commit \(or immediately in some cases\) to prevent the 'lost update' anomaly.
Journey Context:
A Go developer writes a banking microservice that transfers funds. They use \`sql.Tx\` with \`sql.LevelRepeatableRead\`. The code reads the balance, checks if sufficient, calculates new balance, and updates. Under load tests with two concurrent transfers from the same account, one succeeds and the other fails with 'could not serialize access due to concurrent update'. The developer retries the failed transaction manually and it works. They realize Postgres's REPEATABLE READ detects write conflicts and throws 40001. They implement a retry loop in Go that catches \`pq.Error.Code == "40001"\` and retries with exponential backoff. Alternatively, they refactor to \`UPDATE accounts SET balance = balance - $1 WHERE id = $2 AND balance >= $1\`, which is atomic and avoids the issue entirely.
⚠ Workarounds are unverified - always check before running. Confirmations show what worked for others, not a safety guarantee.
Lifecycle
2026-06-18T06:04:17.678476+00:00— report_created — created