risingwavelabs/risingwave · error
bad result from postgres
Error message
bad result from postgres
What it means
The PostgreSQL-backed election client's try_campaign expects the campaign INSERT ... ON CONFLICT ... RETURNING query to return exactly one row. An empty result is rejected with 'bad result from postgres', meaning the upsert did not return the leader row as required.
Solutions
- Verify the connection targets the intended Postgres database and schema, and avoid transaction-mode poolers for the election connection.
- Check the election table exists with the expected primary key and the Postgres version supports the used upsert syntax.
- Look for prior postgres errors in meta logs (deadlock, serialization) and retry election.
- If reproducible on a known-good Postgres, file a bug with the campaign query and driver version.
Defensive patterns
Strategy: retry
Try / catch
match client.try_campaign(service, lock_id).await {
Err(e) if e.to_string().contains("bad result from postgres") => {
tokio::time::sleep(Duration::from_secs(1)).await;
// re-check connection target before retrying
}
other => other?,
} Prevention
- Avoid transaction-mode poolers (PgBouncer) for election connections.
- Verify all meta nodes connect to the same database and schema.
- Test upsert/RETURNING behavior on your exact Postgres version before deployment.
When it happens
Trigger: try_campaign on PostgresElectionClient when the RETURNING clause yields no row — e.g. the ON CONFLICT branch matched but no row was returned due to driver/query behavior or store anomalies.
Common situations: Postgres version or driver incompatibility changing upsert semantics; concurrent truncation of the election table; connecting through a pooler (e.g. PgBouncer transaction mode) that splits the write/read across sessions or databases.
Understand the failure class
Background: EmptyResultError / "no results found": when an API or scraper succeeds but returns zero rows — this error's family across 9 libraries.
Related errors
AI-assisted analysis of risingwavelabs/risingwave@6469eb736d (2026-09-11).
Data as JSON: /api/errors/fbff2d6b6b1a91d4.
Report an issue: GitHub.
Appendix: source
Thrown at src/meta/src/rpc/election/sql.rs:541
END
RETURNING service, id, last_heartbeat;
"#,
table = Self::election_table_name()
),
vec![
Value::from(service_name),
Value::from(id),
// special handling for interval
Value::from(ttl.to_string()),
],
))
.await?;
let row = query_result
.map(|query_result| ElectionRow::from_query_result(&query_result, ""))
.transpose()?;
let row = row.ok_or_else(|| anyhow!("bad result from postgres"))?;
Ok(row)
}
async fn leader(&self, service_name: &str) -> MetaResult<Option<ElectionRow>> {
let query_result = self
.conn
.query_one(Statement::from_sql_and_values(
DatabaseBackend::Postgres,
format!(
r#"SELECT service, id, last_heartbeat FROM {table} WHERE service = $1;"#,
table = Self::election_table_name()
),
vec![Value::from(service_name)],
))
.await?;
let row = query_resultView on GitHub (pinned to 6469eb736d)