risingwavelabs/risingwave · error

upsert stream is not supported as input of {}, plan: {}

Error message

upsert stream is not supported as input of {}, plan:
{}

What it means

This macro checks the stream kind of an operator's input during optimization. RisingWave cannot build certain stream operators (e.g. aggregations, joins that require append-only or non-upsert input) on top of an upsert stream, so it bails with the operator name and the full plan text.

Source

Thrown at src/frontend/src/optimizer/property/stream_kind.rs:123

    }
}

/// Reject upsert stream as input.
macro_rules! reject_upsert_input {
    ($input:expr) => {
        reject_upsert_input!(
            $input,
            std::any::type_name::<Self>().split("::").last().unwrap()
        )
    };

    ($input:expr, $curr:expr) => {{
        use crate::optimizer::plan_node::Explain;
        use crate::optimizer::property::StreamKind;

        let kind = $input.stream_kind();
        if let StreamKind::Upsert = kind {
            risingwave_common::bail!(
                "upsert stream is not supported as input of {}, plan:\n{}",
                $curr,
                $input.explain_to_string()
            );
        }
        kind
    }};
}
pub(crate) use reject_upsert_input;

View on GitHub (pinned to 6469eb736d)

Solutions

  1. Rewrite the query to consume the upsert stream through an operator that supports upsert input (e.g. use an aggregation or a supported sink).
  2. Define the upstream MV without upsert (use an append-only source or add a primary key that makes it append-compatible).
  3. Read the embedded plan text to identify which node produces the upsert kind and restructure above it.
  4. Upgrade or restructure per current docs on supported upsert input operators.

Example fix

-- before: plain count over an upsert MV
CREATE MATERIALIZED VIEW mv2 AS SELECT count(*) FROM upsert_mv;
-- after: force retraction-aware aggregation or use append-only input
CREATE MATERIALIZED VIEW mv2 AS SELECT count(*) FROM (SELECT * FROM upsert_mv) GROUP BY 1; -- or build on append-only source
Defensive patterns

Strategy: validation

Validate before calling

// Before creating an MV over a stream, verify it is not upsert
-- SELECT ... check upstream MV definition; append-only sources only

Try / catch

// Wrap MV creation and read the bail message's plan text to find the upsert node
CREATE MATERIALIZED VIEW ...; -- on failure, inspect plan text in the error for 'Upsert'

Prevention

When it happens

Trigger: Optimizer applying a plan-node macro (`assert...` style) whose macro receives an input whose `stream_kind()` is `StreamKind::Upsert`; typically when a user query composes an operator above an upsert-producing source or MV.

Common situations: Creating an MV or query on top of an upsert materialized view/source with operators that lack upsert input support; version changes that tighten stream-kind requirements.

Understand the failure class

Background: UnsupportedOperationException and "is not supported" errors: when a library deliberately refuses a call — this error's family across 30 libraries.

Related errors


AI-assisted analysis of risingwavelabs/risingwave@6469eb736d (2026-09-11). Data as JSON: /api/errors/a9a2ab03dfbadc3d. Report an issue: GitHub.