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
- Read the inner `message` for the specific mismatch; if it wraps a Fragment/Operator error, fix that root cause first.
- Ensure the ALTER only makes supported changes (adding columns etc.); otherwise recreate the MV/table with the new definition.
- 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
- Restrict ALTERs to changes that keep graph topology comparable
- Test schema changes against production-like plans before applying
- Keep frontend and meta versions in sync
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
- failed to match fragment
- failed to match operator
- sink with auto schema change should have only 1 fragment…
- unsupported job type for replacement
- id not found
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)