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

  1. Upgrade RisingWave to the latest version (likely an internal bug fixed upstream).
  2. Check which statement triggered it and try running it via extended protocol (prepared statement) or psql to reproduce.
  3. Report/inspect the statement type: verify the DML classification in the frontend for that SQL.
  4. 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

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


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.stream

View on GitHub (pinned to 6469eb736d)