neondatabase/neon · error
could not get total_sessions: {}
Error message
could not get total_sessions: {} What it means
The final column conversion in get_database_stats(): stats.try_get("total_sessions") to i64 failed because the 'total_sessions' column is not the BIGINT/INT8 the Rust side expects. The query intentionally casts to ::pg_catalog.bigint, so this error signals query/type drift rather than a bad value. It propagates as a check() error (downtime + reconnect).
Source
Thrown at compute_tools/src/monitor.rs:434
'template1'
);",
&[],
);
let stats = match stats {
Ok(stats) => stats,
Err(e) => {
return Err(anyhow::anyhow!("could not query active_time: {}", e));
}
};
let active_time: f64 = match stats.try_get("total_active_time") {
Ok(active_time) => active_time,
Err(e) => return Err(anyhow::anyhow!("could not get total_active_time: {}", e)),
};
let sessions: i64 = match stats.try_get("total_sessions") {
Ok(sessions) => sessions,
Err(e) => return Err(anyhow::anyhow!("could not get total_sessions: {}", e)),
};
Ok((active_time, sessions))
}
// Figure out the most recent state change time across all client backends.
// If there is currently active backend, timestamp will be `Utc::now()`.
// It can return `None`, which means no client backends exist or we were
// unable to parse the timestamp.
fn get_backends_state_change(cli: &mut Client) -> anyhow::Result<Option<DateTime<Utc>>> {
let mut last_active: Option<DateTime<Utc>> = None;
// Get all running client backends except ourself, use RFC3339 DateTime format.
let backends = cli.query(
"SELECT state, pg_catalog.to_char(state_change, 'YYYY-MM-DD\"T\"HH24:MI:SS.US\"Z\"'::pg_catalog.text) AS state_change
FROM pg_stat_activity
WHERE backend_type OPERATOR(pg_catalog.=) 'client backend'::pg_catalog.text
AND pid OPERATOR(pg_catalog.!=) pg_catalog.pg_backend_pid()
AND usename OPERATOR(pg_catalog.!=) 'cloud_admin'::pg_catalog.name;", // XXX: find a better way to filter other monitors?View on GitHub (pinned to 8f60b04da4)
Solutions
- Restore the explicit cast: coalesce(sum(sessions), 0)::pg_catalog.bigint AS total_sessions
- Read the inner FromSql error for the concrete type mismatch
- Confirm the 'total_sessions' alias still exists in the query
- Add a regression test asserting the (f64, i64) tuple round-trips
Example fix
// before "pg_catalog.coalesce(pg_catalog.sum(sessions), 0) AS total_sessions" // after "pg_catalog.coalesce(pg_catalog.sum(sessions), 0)::pg_catalog.bigint AS total_sessions"
Defensive patterns
Strategy: validation
Validate before calling
// keep the explicit cast so the column is always BIGINT on the wire const GET_DB_STATS_SQL: &str = "... pg_catalog.coalesce(pg_catalog.sum(sessions), 0)::pg_catalog.bigint AS total_sessions ...";
Type guard
fn col_is_int8(row: &postgres::Row, col: &str) -> bool {
row.columns()
.iter()
.any(|c| c.name() == col && *c.type_() == postgres::types::Type::INT8)
} Try / catch
let sessions: i64 = match stats.try_get("total_sessions") {
Ok(v) => v,
Err(e) => {
warn!("total_sessions unreadable, skipping stats check: {e}");
return Ok(());
}
}; Prevention
- Never remove the ::bigint cast when editing get_database_stats
- Verify aliases survive query refactors
- Add a unit/integration test for the stats tuple types
When it happens
Trigger: Column 'total_sessions' not of SQL type INT8: cast removed in a query edit, alias renamed, or a server returning a different type for sum(sessions).
Common situations: Editing monitor SQL without updating conversions; Postgres version changes in type inference for sum() over pg_stat_database columns.
Related errors
- could not get total_active_time: {}
- could not get database statistics: {}
- failed to parse 'pg_stat_subscription' count: {}
- failed to parse autovacuum workers count: {}
- could not query active_time: {}
AI-assisted analysis of neondatabase/neon@8f60b04da4 (2026-08-16).
Data as JSON: /api/errors/fb6b2d23cd66fda2.
Report an issue: GitHub.