{"record":{"id":"c0c8b30a50a6c77d","repo":"neondatabase/neon","slug":"could-not-get-database-statistics","errorCode":null,"errorMessage":"could not get database statistics: {}","messagePattern":"could not get database statistics: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"compute_tools/src/monitor.rs","lineNumber":239,"sourceCode":"                    }\n                    self.active_time = Some(active_time);\n\n                    if let Some(prev_sessions) = self.sessions {\n                        if sessions != prev_sessions {\n                            detected_activity = true;\n                        }\n                    }\n                    self.sessions = Some(sessions);\n\n                    if detected_activity {\n                        // Update the last active time and continue, we don't need to\n                        // check backends state change.\n                        self.last_active = Some(Utc::now());\n                        return Ok(());\n                    }\n                }\n                Err(e) => {\n                    return Err(anyhow::anyhow!(\"could not get database statistics: {}\", e));\n                }\n            }\n        }\n\n        // If database statistics are the same, check all backends for state changes.\n        // Maybe there are some with more recent activity. `get_backends_state_change()`\n        // can return None or stale timestamp, so it's `compute.update_last_active()`\n        // responsibility to check if the new timestamp is more recent than the current one.\n        // This helps us to discover new sessions that have not done anything yet.\n        match get_backends_state_change(cli) {\n            Ok(last_active) => match (last_active, self.last_active) {\n                (Some(last_active), Some(prev_last_active)) => {\n                    if last_active > prev_last_active {\n                        self.last_active = Some(last_active);\n                        return Ok(());\n                    }\n                }\n                (Some(last_active), None) => {","sourceCodeStart":221,"sourceCodeEnd":257,"githubUrl":"https://github.com/neondatabase/neon/blob/8f60b04da47ffefe0e52bda2440134b42874eb75/compute_tools/src/monitor.rs#L221-L257","documentation":"Thrown by the compute_ctl activity monitor thread (ComputeMonitor::check) when get_database_stats() fails. That helper runs one SELECT over pg_catalog.pg_stat_database summing active_time and sessions for all non-system databases, so this error means tokio_postgres could not execute the query or fetch the row. The monitor treats any such failure as suspicious (broken connection, statement timeout, invalid data), reports Postgres downtime via the PG_CURR_DOWNTIME_MS/PG_TOTAL_DOWNTIME_MS metrics, reconnects, and retries on the next 500ms tick. It only executes when the ActivityMonitorExperimental feature flag is enabled.","triggerScenarios":"cli.query_one() on pg_stat_database returns Err: the TCP connection silently died (the code comments note queries fail with 'connection closed' while Client::is_closed() still returns false), a statement_timeout aborted the SELECT, or Postgres restarted/crashed mid-query.","commonSituations":"Autoscaled Neon computes being suspended or restarted while the monitor polls every 500ms; heavy user load causing timeouts on catalog statistics views; local dev against a Postgres that was killed without closing its sockets.","solutions":["Read the nested error text after the colon - it carries the underlying tokio_postgres error and SQLSTATE (connection closed, statement timeout, etc.) which identifies the real cause","Check that Postgres is accepting connections on the monitor's connstr and that the compute reached the Running state (wait_for_postgres_start already gates this)","Tolerate isolated occurrences: the monitor automatically reconnects (client = conf.connect(NoTls)) on the next iteration, so single failures during suspend/resume are expected; only chase repeated ones","If it repeats with timeouts, raise statement_timeout for the cloud_admin monitor role or reduce load"],"exampleFix":null,"handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"// in the polling loop: log, count downtime, reconnect, continue\nmatch monitor.check(cli) {\n    Ok(_) => monitor.report_up(),\n    Err(e) => {\n        error!(\"could not check Postgres: {e}\");\n        monitor.report_down();\n        client = conf.connect(NoTls); // fresh connection for the next tick\n    }\n}","preventionTips":["Keep the monitor's connstr stable and only start monitoring once the compute is in Running state","Alert on consecutive monitor errors, not single occurrences - the loop self-heals every 500ms","Avoid statement_timeout values aggressive enough to kill catalog-statistics queries"],"tags":["postgres","pg-stat-database","monitoring","rust","connection"],"backgroundTag":"postgres-query-failed","analyzedSha":"8f60b04da47ffefe0e52bda2440134b42874eb75","analyzedAt":"2026-08-16T23:39:28.135Z","schemaVersion":2},"datasetVersion":"2026-08-17T04:17:16.089Z"}