risingwavelabs/risingwave · error
bad result from sqlite
Error message
bad result from sqlite
What it means
The SQLite-backed meta election implementation runs an INSERT ... ON CONFLICT style campaign query and expects exactly one row back identifying the leader. When the query succeeds but returns no row, try_campaign rejects with 'bad result from sqlite'. This indicates the election query's contract (a guaranteed row) was violated by the store.
Solutions
- Verify the sqlite meta store file is intact and not shared by multiple meta processes; use a single meta node with sqlite.
- Check meta logs for preceding sqlite errors (lock, corruption) and restore the meta store from backup if corrupted.
- Restart the meta node to re-run election and repopulate the row.
- If reproducible, file a bug: the campaign query should always return a row, so this signals a driver or query defect.
Defensive patterns
Strategy: retry
Try / catch
// detect the empty election row and retry campaign with backoff
match client.try_campaign(service, lock_id).await {
Err(e) if e.to_string().contains("bad result from sqlite") => {
tokio::time::sleep(Duration::from_secs(1)).await;
// verify store integrity before retrying
}
other => other?,
} Prevention
- Use sqlite only for single-node meta deployments.
- Monitor meta store file integrity and disk health.
- Pin and test the sqlx/driver version against your sqlite version.
When it happens
Trigger: try_campaign on SqliteElectionClient when the campaign upsert query returns an empty result set, i.e. the row inserted/updated cannot be read back.
Common situations: Corrupted or concurrently wiped sqlite meta store file; running multiple meta nodes against a sqlite store not intended for HA; sqlite driver/behavior changes returning no rows for the upsert.
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/3fdc0b35dd9460bb.
Report an issue: GitHub.
Appendix: source
Thrown at src/meta/src/rpc/election/sql.rs:191
END,
last_heartbeat = CASE
WHEN DATETIME({table}.last_heartbeat, '+' || $3 || ' seconds') < CURRENT_TIMESTAMP THEN EXCLUDED.last_heartbeat
WHEN {table}.id = EXCLUDED.id THEN EXCLUDED.last_heartbeat
ELSE {table}.last_heartbeat
END
RETURNING service, id, last_heartbeat;
"#,
table = Self::election_table_name()
),
vec![Value::from(service_name), Value::from(id), Value::from(ttl)],
))
.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 sqlite"))?;
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::Sqlite,
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)