risingwavelabs/risingwave · error · PsqlError
no affected rows in output
Error message
no affected rows in output
What it means
For DML statements without RETURNING, `inner_process_query_msg_one_stmt` expects the first row set of the simple-query response to contain the affected-rows count (as produced by the `pg affected rows` statement). If the values stream yields nothing, the protocol layer cannot report a row count and returns this `PsqlError::Uncategorized`.
Solutions
- Upgrade RisingWave to the latest version (likely an internal bug fixed upstream).
- Check which statement triggered it and try running it via extended protocol (prepared statement) or psql to reproduce.
- Report/inspect the statement type: verify the DML classification in the frontend for that SQL.
- As a workaround, add RETURNING or restructure the statement so a row set is produced.
Example fix
null
Defensive patterns
Strategy: try-catch
Try / catch
match run_dml(stmt) {
Err(PsqlError::Uncategorized(e)) if e.to_string().contains("no affected rows in output") => {
log::error!("affected-rows row set missing for {stmt}; check RW version");
}
other => other?,
} Prevention
- Keep RisingWave updated — this is an internal invariant bug, not user error.
- Test DML paths of your client tooling against the target RW version.
- Prefer extended protocol for DML if simple protocol misbehaves.
When it happens
Trigger: Executing an INSERT/UPDATE/DELETE via pgwire where the batch engine produces no row set for the affected-rows output — e.g. internal executor bug, statement misclassified as DML, or empty result from `values_stream()`.
Common situations: Client running simple-protocol DML through a proxy or tool against a RisingWave version with an affected-rows regression; hitting an unhandled statement type reported as DML.
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
- BatchGapFill is not implemented yet
- Expr error
- insert should always be converted to batch plan
- `LogicalNow` can only be converted to stream
- query_epoch not set in distributed lookup join
AI-assisted analysis of risingwavelabs/risingwave@6469eb736d (2026-09-11).
Data as JSON: /api/errors/e2053a3b1b908954.
Report an issue: GitHub.
Appendix: source
Thrown at src/utils/pgwire/src/pg_protocol.rs:957
.await?;
rows_cnt += 1;
}
}
// Run the callback before sending the `CommandComplete` message.
res.run_callback().await?;
self.stream
.write_no_flush(BeMessage::CommandComplete(BeCommandCompleteMessage {
stmt_type: res.stmt_type(),
rows_cnt,
}))?;
} else if res.stmt_type().is_dml() && !res.stmt_type().is_returning() {
let first_row_set = res.values_stream().next().await;
let first_row_set = match first_row_set {
None => {
return Err(PsqlError::Uncategorized(
anyhow::anyhow!("no affected rows in output").into(),
));
}
Some(row) => row.map_err(PsqlError::SimpleQueryError)?,
};
let affected_rows_str = first_row_set[0].values()[0]
.as_ref()
.expect("compute node should return affected rows in output");
assert!(matches!(res.row_cnt_format(), Some(Format::Text)));
let affected_rows_cnt = String::from_utf8(affected_rows_str.to_vec())
.unwrap()
.parse()
.unwrap_or_default();
// Run the callback before sending the `CommandComplete` message.
res.run_callback().await?;
self.streamView on GitHub (pinned to 6469eb736d)