psql exits 0 after an error. It finished the file.
The common read: A statement in my script errored, so the process exits nonzero and the runner that gates on exit codes catches it.
The exit code is a claim about psql, not about your statements. Postgres documents four values and each one names a condition of the client process: 0 if it finished normally, 1 if a fatal error of its own occurs such as out of memory or file not found, 2 if the connection to the server went bad and the session was not interactive, and 3 if an error occurred in a script and the variable ON_ERROR_STOP was set.
Read that list for what is missing. A statement the server rejected is not on it, and finishing normally means reaching the end of the input, because the documented default is that command processing continues after an error.
So the transcript below holds a rejected UPDATE, a statement that ran after it, and a process that told the shell everything was fine. The account that should have moved to pro stays on standard forever, since nothing will ever re-run that statement: the run it belonged to is recorded as a success. That arrives as a finance question about one invoice a month later, filed against the billing report, and the report is correct. The error was printed. Nothing read it.
