neondatabase/neon · error
could not get total_active_time: {}
Error message
could not get total_active_time: {} What it means
After get_database_stats()' query succeeded, stats.try_get("total_active_time") to f64 failed - the 'total_active_time' column could not be converted to f64. The query explicitly casts to ::pg_catalog.float8 precisely to match Rust's f64, so seeing this error implies the query text or expected types drifted (someone removed the cast, the column was renamed, or a non-float8 type arrived). It surfaces as a monitor check() failure.
Source
Thrown at compute_tools/src/monitor.rs:429
pg_catalog.coalesce(pg_catalog.sum(sessions), 0)::pg_catalog.bigint AS total_sessions
FROM pg_catalog.pg_stat_database
WHERE datname NOT IN (
'postgres',
'template0',
'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(View on GitHub (pinned to 8f60b04da4)
Solutions
- Restore the explicit cast: coalesce(sum(active_time), 0.0)::pg_catalog.float8 AS total_active_time
- Check the inner FromSql error for expected vs actual type
- Verify the column alias 'total_active_time' is still produced
- Keep explicit pg_catalog casts on all monitor statistics columns (the codebase convention)
Example fix
// before "pg_catalog.coalesce(pg_catalog.sum(active_time), 0.0) AS total_active_time" // after "pg_catalog.coalesce(pg_catalog.sum(active_time), 0.0)::pg_catalog.float8 AS total_active_time"
Defensive patterns
Strategy: validation
Validate before calling
// keep the explicit cast so the column is always FLOAT8 on the wire const GET_DB_STATS_SQL: &str = "SELECT pg_catalog.coalesce(pg_catalog.sum(active_time), 0.0)::pg_catalog.float8 AS total_active_time ...";
Type guard
fn col_is_float8(row: &postgres::Row, col: &str) -> bool {
row.columns()
.iter()
.any(|c| c.name() == col && *c.type_() == postgres::types::Type::FLOAT8)
} Try / catch
let active_time: f64 = match stats.try_get("total_active_time") {
Ok(v) => v,
Err(e) => {
warn!("total_active_time unreadable, skipping stats check: {e}");
return Ok(()); // don't fail the whole monitor tick
}
}; Prevention
- Never remove the ::float8 cast when editing get_database_stats
- Keep column aliases and Rust conversions in sync in one commit
- Regression-test the (f64, i64) tuple against a real Postgres
When it happens
Trigger: The selected column is not SQL type FLOAT8: the ::float8 cast was dropped in a query edit, the alias changed, or an exotic server returns a different type for sum(active_time).
Common situations: Refactoring the monitor query without keeping conversions aligned; running against Postgres versions or forks where sum()/coalesce() type inference differs.
Related errors
- could not get total_sessions: {}
- 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/32db97953dd2f54c.
Report an issue: GitHub.