risingwavelabs/risingwave · error · Error

failed to match graph

Error message

failed to match graph: {message}

What it means

This is the top-level variant of the `state_match::Error` enum used when matching an old and new stream graph for state table migration (schema change). `Graph` carries a free-form message describing why the whole graph could not be matched, typically produced via context wrapping lower-level Fragment/Operator errors or direct graph-level checks.

Solutions

  1. Read the inner `message` for the specific mismatch; if it wraps a Fragment/Operator error, fix that root cause first.
  2. Ensure the ALTER only makes supported changes (adding columns etc.); otherwise recreate the MV/table with the new definition.
  3. Align frontend/meta versions so both sides generate structurally comparable graphs.
Defensive patterns

Strategy: try-catch

Validate before calling

null

Type guard

if let state_match::Error::Graph { message } = err {
    // handle graph-level mismatch
}

Try / catch

match state_match::match_graph(&old, &new) {
    Err(state_match::Error::Graph { message }) => {
        tracing::warn!("schema change unsupported: {}", message);
        // fall back to full rebuild of the job
    }
    Err(e) => return Err(e.into()),
    Ok(tables) => apply(tables),
}

Prevention

When it happens

Trigger: Running a schema-change state-table match where a graph-level check fails — e.g. the new graph has a different structure that cannot be paired with the old graph, or a fragment-level failure is lifted to the graph level with context.

Common situations: ALTER statements whose new plan topology diverges too much from the old plan (unsupported schema changes), version upgrades changing operator layout so old and new graphs no longer correspond.

Understand the failure class

Background: Schema validation failed / invalid input schema: payload rejected because its shape doesn't match the expected schema — this error's family across 28 libraries.

Related errors


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

Appendix: source

Thrown at src/meta/src/stream/stream_graph/state_match.rs:56

    fn from(node: &StreamNode) -> Self {
        let id = node.operator_id;
        let identity = &node.identity;
        let body = node.node_body.as_ref().unwrap();

        Self(format!("{}({}, {})", body, id, identity).into_boxed_str())
    }
}

impl std::fmt::Display for StreamNodeDesc {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "{}", self.0)
    }
}

/// Error type for failed state table matching.
#[derive(thiserror::Error, thiserror_ext::Macro, thiserror_ext::ReportDebug)]
pub(crate) enum Error {
    #[error("failed to match graph: {message}")]
    Graph { message: String },

    #[error("failed to match fragment {id}: {message}")]
    Fragment {
        source: Option<Box<Error>>,
        id: Id,
        message: String,
    },

    #[error("failed to match operator {from} to {to}: {message}")]
    Operator {
        from: StreamNodeDesc,
        to: StreamNodeDesc,
        message: String,
    },
}

type Result<T, E = Error> = std::result::Result<T, E>;

View on GitHub (pinned to 6469eb736d)