{"record":{"id":"6508d4731a982169","repo":"risingwavelabs/risingwave","slug":"project-node-must-have-exactly-1-input-for-auto-sc","errorCode":null,"errorMessage":"Project node must have exactly 1 input for auto schema change, but got {:?}","messagePattern":"Project node must have exactly 1 input for auto schema change, but got (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/meta/src/stream/stream_graph/fragment.rs","lineNumber":419,"sourceCode":"        return Err(anyhow!(\n            \"sink with auto schema change should have only 1 fragment, but got {:?}\",\n            fragments.len()\n        )\n        .into());\n    }\n    let (_, fragment) = fragments.first_key_value().expect(\"non-empty\");\n    let sink_node = &fragment.nodes;\n    let PbNodeBody::Sink(_) = sink_node.node_body.as_ref().unwrap() else {\n        return Err(anyhow!(\"expect PbNodeBody::Sink but got: {:?}\", sink_node.node_body).into());\n    };\n    let [stream_input_node] = sink_node.input.as_slice() else {\n        panic!(\"Sink has more than 1 input: {:?}\", sink_node.input);\n    };\n    let stream_scan_node = match stream_input_node.node_body.as_ref().unwrap() {\n        PbNodeBody::StreamScan(_) => stream_input_node,\n        PbNodeBody::Project(_) => {\n            let [stream_scan_node] = stream_input_node.input.as_slice() else {\n                return Err(anyhow!(\n                    \"Project node must have exactly 1 input for auto schema change, but got {:?}\",\n                    stream_input_node.input.len()\n                )\n                .into());\n            };\n            stream_scan_node\n        }\n        _ => {\n            return Err(anyhow!(\n                \"expect PbNodeBody::StreamScan or PbNodeBody::Project but got: {:?}\",\n                stream_input_node.node_body\n            )\n            .into());\n        }\n    };\n    let PbNodeBody::StreamScan(scan) = stream_scan_node.node_body.as_ref().unwrap() else {\n        return Err(anyhow!(\n            \"expect PbNodeBody::StreamScan but got: {:?}\",","sourceCodeStart":401,"sourceCodeEnd":437,"githubUrl":"https://github.com/risingwavelabs/risingwave/blob/6469eb736d691e8e9b8a419a57edd6429ca77417/src/meta/src/stream/stream_graph/fragment.rs#L401-L437","documentation":"When the sink fragment's node under the sink is a Project (used for auto schema change), the code expects that Project to have exactly one input (the StreamScan). If the Project node has any other number of inputs, this error is returned. The message misleadingly formats `stream_input_node.input.len()` with `{:?}`.","triggerScenarios":"`generate_streaming_job` -> `check_sink_fragments_support_refresh_schema` encountering a `PbNodeBody::Project` node between the sink and the stream scan whose `input` slice is not exactly length 1 (0 or 2+ inputs).","commonSituations":"Auto schema change (schema registry evolution, e.g. adding/removing columns on a table being backfilled into a sink) producing an unexpected multi-input project; corrupted or hand-edited fragment metadata; frontend/meta version mismatch.","solutions":["Confirm the sink was created with schema refresh supported (single-arrangement-backfill source); recreate the sink if its graph was built by an older planner.","Check frontend and meta versions match; redeploy with aligned binaries.","Inspect the fragment graph in meta logs to see the actual input count of the project node.","Report to RisingWave if a supported `CREATE SINK` triggers it."],"exampleFix":"// before\nlet [stream_scan_node] = stream_input_node.input.as_slice() else { ... };\n// after: handle the shape explicitly upstream\nif stream_input_node.input.len() != 1 {\n    return Err(anyhow!(\"unexpected project fan-in {} for schema refresh\", stream_input_node.input.len()).into());\n}\nlet [stream_scan_node] = stream_input_node.input.as_slice() else { unreachable!() };","handlingStrategy":"validation","validationCode":"if let Some(PbNodeBody::Project(_)) = stream_input_node.node_body.as_ref() {\n    assert_eq!(stream_input_node.input.len(), 1, \"project fan-in must be 1\");\n}","typeGuard":"fn is_single_input_project(node: &StreamNode) -> Option<&StreamNode> {\n    match (node.node_body.as_ref(), node.input.as_slice()) {\n        (Some(PbNodeBody::Project(_)), [only]) => Some(only),\n        _ => None,\n    }\n}","tryCatchPattern":null,"preventionTips":["Only rely on auto schema refresh for the canonical sink-over-backfill topology.","Diff fragment graphs after planner upgrades in staging.","Avoid hand-editing or migrating fragment metadata across versions."],"tags":["risingwave","streaming-graph","schema-change","meta-node"],"backgroundTag":"unexpected-response-shape","analyzedSha":"6469eb736d691e8e9b8a419a57edd6429ca77417","analyzedAt":"2026-09-11T21:06:21.487Z","contentChangedAt":"2026-09-11T21:06:21.487Z","schemaVersion":2},"datasetVersion":"2026-09-14T16:17:12.679Z"}