At a glance
The share of statements that failed instead of completing, over the last five minutes. A healthy production PostgreSQL instance runs at effectively 0%: well-behaved applications do not send statements that error. Any sustained reading above noise floor means something is broken, a bad deploy shipping malformed SQL, a migration half-applied, a permission revoked, a constraint being violated, or the server itself refusing connections because it is out of slots. This is the fastest way to catch a deploy that broke the database contract.
Calculation
The gauge is the ratio of failed statements to attempted statements over the rolling five-minute window:attempts_5mis the change inpg_stat_statements.calls(preferred) or, as a fallback, the change inxact_commit + xact_rollbackfrompg_stat_database.errors_5mis built from several signals, most reliable first: log-derivedERRORandFATALline counts where log access is available; the change inxact_rollback(a rolled-back transaction usually, though not always, indicates an error); and thedeadlockscounter frompg_stat_databasefor the deadlock contribution.
xact_rollback signal needs care. Not every rollback is an error: an application can deliberately ROLLBACK a transaction it no longer needs. To avoid overcounting, Vortex IQ treats xact_rollback as an upper-bound estimate and, where log access is configured, prefers the precise ERROR/FATAL counts from the server log. Without log access the card still works but reads a slightly conservative (higher) error rate, which is the safe direction for an alerting card.
Connection refusals deserve special mention. When the connection pool saturates and PostgreSQL returns FATAL: sorry, too many clients already, those refusals surface here as errors, which is why an error-rate spike and a saturation spike so often arrive together. See Connection Pool Saturation %.
Worked example
A platform team ships a schema migration to a PostgreSQL 15 primary at 14:20 BST on 18 Apr 26. The migration adds aNOT NULL column to the orders table but the application deploy that populates the column lags ten minutes behind. In that gap, every insert that omits the new column fails.
The baseline is essentially zero (a handful of incidental errors per five minutes is normal). At 14:20 the migration lands and the rate jumps to 2.07%, well over the 1% alert threshold. The gauge turns red and pages on-call.
The DBA’s first move is to find out which error, because the percentage alone does not name the cause:
23502 not_null_violation, pointing straight at the new NOT NULL column. The cause is now obvious: the column was made NOT NULL before the code that fills it shipped, a classic migration-ordering mistake. Every order placed in the gap is failing at the database layer, which means real customers are seeing checkout errors.
NOT NULL constraint (ALTER TABLE orders ALTER COLUMN x DROP NOT NULL;) to stop the bleeding immediately, or fast-forward the lagging application deploy. The team drops the constraint, the rate falls back to baseline within one window, and they re-apply NOT NULL only after the populating code is live.
Two takeaways the team writes into their runbook:
- A non-zero error rate on production PostgreSQL is never normal. Unlike latency (which has a healthy non-zero range), a healthy error rate is approximately 0%. Any sustained reading above the noise floor is a defect to be explained, not a number to be tuned.
- The percentage tells you something is wrong; the SQLSTATE class tells you what. Always follow an error-rate alert with a look at the dominant error class.
23xxxis a constraint or data problem (often a deploy),40xxxis concurrency (deadlock or serialization),42xxxis a SQL or schema problem (bad query or missing object),53300istoo_many_connections(a saturation event in disguise).
Sibling cards
Reconciling against the source
Where to look in PostgreSQL itself:The ground truth for errors is the server log. WithWhy our number may legitimately differ from a hand count:log_min_messages = error(or finer) andlog_min_error_statement = error, everyERRORandFATALis logged with its SQLSTATE and the offending statement. Grep the log for the alert window and group by SQLSTATE to see the dominant class. For the rollback-based baseline, runSELECT xact_commit, xact_rollback, deadlocks FROM pg_stat_database WHERE datname = current_database();twice across the window and difference it. For the deadlock contribution specifically, thedeadlockscounter in the same view is exact. On a managed service, the console exposes errors through logs and metrics: AWS RDS publishes PostgreSQL logs to CloudWatch Logs and exposes Performance Insights, Google Cloud SQL surfacesERRORlog entries in Cloud Logging, and Azure Database for PostgreSQL exposes server logs and thepg_stat_databasecounters.
Known limitations / FAQs
What is a normal error rate? On a healthy production OLTP instance, effectively 0%, a handful of incidental errors per five minutes from edge cases. Unlike latency, there is no healthy non-zero band: any sustained reading above the noise floor is a defect to be explained. That is why the alert sits at 1% rather than at some multiple of a baseline. The rate spiked but my queries look fine. What else could it be? Connection refusals. When the pool saturates, PostgreSQL returnsFATAL: sorry, too many clients already, and those count as errors here. An error-rate spike that arrives alongside a Connection Pool Saturation % spike is almost always refusals, not bad SQL. Fix the pool and the error rate clears.
How do I find out which error is driving the percentage?
The card gives you the rate; the server log gives you the cause. Grep the log for the alert window and group by SQLSTATE class. 23xxx is constraints or data (usually a deploy), 40xxx is concurrency (deadlock or serialization), 42xxx is a SQL or schema fault, 53300 is too many connections. The class names the fix far faster than the percentage alone.
Does a query that returns zero rows count as an error?
No. Returning zero rows is a successful query that happened to match nothing. Only statements the server actively rejected or aborted with an ERROR or FATAL count. Likewise a WARNING or NOTICE is not an error and does not affect this card.
Why might Vortex IQ read a higher error rate than my log shows?
If Vortex IQ does not have log access, it falls back to xact_rollback as an upper-bound estimate, and deliberate application rollbacks (transactions the app chose to abandon, not failures) inflate it. Granting the connector log access lets it use precise ERROR/FATAL counts and the two numbers converge.
A client-side timeout fired but the server completed the query. Does that count?
No, not on this card. If the server actually executed the statement successfully, it is a success from PostgreSQL’s point of view even though the client gave up waiting. That scenario shows up on the latency cards (Query Latency p95 (ms) and p99), not here. Server-side statement_timeout cancellations, by contrast, do count, because the server aborted the statement.
Can I change the 1% threshold?
Yes, in the Alert Rules tab per profile. Most teams should tighten it, not loosen it: for a critical OLTP primary, 1% is already a lot of failed customer actions, and alerting at 0.1% or even on any sustained non-zero reading is reasonable. A batch or analytics database that tolerates occasional expected failures might justify a higher floor.