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

  1. Restore the explicit cast: coalesce(sum(active_time), 0.0)::pg_catalog.float8 AS total_active_time
  2. Check the inner FromSql error for expected vs actual type
  3. Verify the column alias 'total_active_time' is still produced
  4. 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

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


AI-assisted analysis of neondatabase/neon@8f60b04da4 (2026-08-16). Data as JSON: /api/errors/32db97953dd2f54c. Report an issue: GitHub.