neondatabase/neon · error
expected 1 query results, but got {}
Error message
expected 1 query results, but got {} What it means
The availability checker runs a single INSERT ... ON CONFLICT DO UPDATE via simple_query and asserts that exactly one result set comes back. PostgreSQL/tokio-postgres returned zero or more than one result, so the invariant 'one statement, one result' was violated and the check fails. It is an internal sanity check on the driver/protocol behavior rather than a database fault.
Source
Thrown at compute_tools/src/checker.rs:34
}
// The connection object performs the actual communication with the database,
// so spawn it off to run on its own.
tokio::spawn(async move {
if let Err(e) = connection.await {
error!("connection error: {}", e);
}
});
let query = "
INSERT INTO public.health_check VALUES (1, pg_catalog.now())
ON CONFLICT (id) DO UPDATE
SET updated_at = pg_catalog.now();";
match client.simple_query(query).await {
Result::Ok(result) => {
if result.len() != 1 {
return Err(anyhow::anyhow!(
"expected 1 query results, but got {}",
result.len()
));
}
}
Err(err) => {
if let Some(state) = err.code() {
if state == &tokio_postgres::error::SqlState::DISK_FULL {
warn!("Tenant disk is full");
return Ok(());
}
}
return Err(err.into());
}
}
Ok(())
}View on GitHub (pinned to 8f60b04da4)
Solutions
- Log and inspect result.len() and the returned command tags to see which side of 1 you got
- Confirm the query string is still exactly one statement (no semicolons appended)
- Pin/check the tokio-postgres version in Cargo.lock against the one this invariant was written for
- If driver behavior legitimately changed, relax the check to len() >= 1 or check that at least one entry is a result row
Example fix
// before
if result.len() != 1 {
return Err(anyhow!("expected 1 query results, but got {}", result.len()));
}
// after
if !result.iter().any(|r| matches!(r, SimpleQueryMessage::CommandComplete(_))) {
return Err(anyhow!("health_check INSERT produced no completed command, got {} messages", result.len()));
} Defensive patterns
Strategy: validation
Validate before calling
// Assert single-statement, single-result shape before trusting the check
static EXPECTED_STMTS: usize = 1;
if query.matches(';').count() > EXPECTED_STMTS { anyhow::bail!("checker query must stay a single statement"); } Try / catch
match client.simple_query(query).await {
Ok(results) if results.len() == 1 => Ok(()),
Ok(results) => { warn!("unexpected {} results", results.len()); Ok(()) } // degrade to warning, do not fail availability
Err(err) => { /* existing SqlState::DISK_FULL special case, else propagate */ }
} Prevention
- Add a unit/integration test running the exact checker query against the pinned tokio-postgres version
- Pin tokio-postgres in Cargo.lock and re-run checker tests on any bump
- Keep the health-check statement as one statement; reject edits that append statements
When it happens
Trigger: client.simple_query("INSERT INTO public.health_check VALUES (1, now()) ON CONFLICT (id) DO UPDATE SET updated_at = now()") returns a Vec whose len() != 1: an empty response (degraded connection, mid-protocol failure) or multiple responses (query string grew to several statements, driver behavior change across tokio-postgres versions).
Common situations: tokio-postgres upgraded to a version that batches/splits simple query results differently; someone edits the query into a multi-statement string; a proxy (pgbouncer in statement mode) splitting or rewriting the statement; flaky connection returning an empty result set.
Related errors
- connection to postgres closed
- process exited with {status}
- postgres --sync-safekeepers exited with non-zero status: {}.
- could not get database statistics: {}
- could not get backends state change: {}
AI-assisted analysis of neondatabase/neon@8f60b04da4 (2026-08-16).
Data as JSON: /api/errors/f6d9b81a041d9730.
Report an issue: GitHub.