neondatabase/neon · error
failed to parse 'pg_stat_subscription' count: {}
Error message
failed to parse 'pg_stat_subscription' count: {} What it means
The subscription-count query succeeded, but row.try_get::<&str, i64>("count") failed - the returned 'count' column could not be converted to i64. count(*) normally returns int8 which maps to i64, so this indicates schema/type drift (a different wire type or missing column) rather than a value problem. It propagates out of check() as a monitor error (downtime + reconnect).
Source
Thrown at compute_tools/src/monitor.rs:309
return Err(anyhow::anyhow!("failed to get list of walsenders: {}", e));
}
}
// Don't suspend compute if there is an active logical replication subscription
//
// `where pid is not null` – to filter out read only computes and subscription on branches
const LOGICAL_SUBSCRIPTIONS_QUERY: &str =
"select count(*) from pg_stat_subscription where pid is not null;";
match cli.query_one(LOGICAL_SUBSCRIPTIONS_QUERY, &[]) {
Ok(row) => match row.try_get::<&str, i64>("count") {
Ok(num_subscribers) => {
if num_subscribers > 0 {
self.last_active = Some(Utc::now());
return Ok(());
}
}
Err(e) => {
return Err(anyhow::anyhow!(
"failed to parse 'pg_stat_subscription' count: {}",
e
));
}
},
Err(e) => {
return Err(anyhow::anyhow!(
"failed to get list of active logical replication subscriptions: {}",
e
));
}
}
// Do not suspend compute if autovacuum is running
const AUTOVACUUM_COUNT_QUERY: &str =
"select count(*) from pg_stat_activity where backend_type = 'autovacuum worker'";
match cli.query_one(AUTOVACUUM_COUNT_QUERY, &[]) {
Ok(r) => match r.try_get::<&str, i64>("count") {View on GitHub (pinned to 8f60b04da4)
Solutions
- Add an explicit SQL cast so the wire type always matches: `count(*)::pg_catalog.bigint`
- Check the inner FromSql error text - it names the expected versus actual type
- Verify the exact query still selects a column aliased 'count' on your PG version
- Compare with get_database_stats which already uses explicit ::float8/::bigint casts for this reason
Example fix
// before
const LOGICAL_SUBSCRIPTIONS_QUERY: &str =
"select count(*) from pg_stat_subscription where pid is not null;";
// after
const LOGICAL_SUBSCRIPTIONS_QUERY: &str =
"select count(*)::pg_catalog.bigint from pg_stat_subscription where pid is not null;" Defensive patterns
Strategy: validation
Validate before calling
// pin the wire type in SQL so try_get::<_, i64> cannot mismatch
const LOGICAL_SUBSCRIPTIONS_QUERY: &str =
"select count(*)::pg_catalog.bigint from pg_stat_subscription where pid is not null;"; Type guard
fn count_is_int8(row: &postgres::Row, col: &str) -> bool {
row.columns()
.iter()
.any(|c| c.name() == col && *c.type_() == postgres::types::Type::INT8)
} Try / catch
match row.try_get::<&str, i64>("count") {
Ok(n) => { /* use n */ }
Err(e) => warn!("skipping subscription count, unexpected type: {e}"), // don't fail the whole check
} Prevention
- Always cast count() to ::bigint in SQL when reading it as i64
- Keep explicit pg_catalog casts on every monitor statistics column
- Integration-test monitor queries against the shipped Postgres version
When it happens
Trigger: try_get type mismatch: the 'count' column's SQL type is not Type::INT8, the column is missing/renamed, or a PG version/extension changes the output type of the pg_stat_subscription count query.
Common situations: Running against an unusual Postgres build or a major-version upgrade that altered system-view output types; editing the monitor query without keeping the Rust conversion in sync.
Understand the failure class
- Parsing and encoding errors: unexpected token, malformed input — why parsers reject input and how to find the real culprit.
Related errors
- failed to get list of active logical replication subscriptio
- failed to parse autovacuum workers count: {}
- could not get total_active_time: {}
- could not get total_sessions: {}
- connection to postgres closed
AI-assisted analysis of neondatabase/neon@8f60b04da4 (2026-08-16).
Data as JSON: /api/errors/a981036eec84fc48.
Report an issue: GitHub.