{"record":{"id":"17dd88867291130a","repo":"risingwavelabs/risingwave","slug":"backfill-order-strategy-has-a-cycle","errorCode":null,"errorMessage":"Backfill order strategy has a cycle","messagePattern":"Backfill order strategy has a cycle","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/frontend/src/optimizer/backfill_order_strategy.rs","lineNumber":341,"sourceCode":"                    start_name\n                );\n            };\n            let Some(end_scanned_relation_ids) = scanned_relation_ids.get(&end_relation_id) else {\n                bail!(\n                    \"Table or source '{}' specified in backfill_order is not used in the query\",\n                    end_name\n                );\n            };\n\n            for start_scanned_relation_id in start_scanned_relation_ids {\n                order\n                    .entry(*start_scanned_relation_id)\n                    .or_default()\n                    .extend(end_scanned_relation_ids.iter().copied());\n            }\n        }\n        if has_cycle(&order) {\n            bail!(\"Backfill order strategy has a cycle\");\n        }\n        Ok(order)\n    }\n}\n\nmod common {\n    use std::collections::{HashMap, HashSet};\n\n    use risingwave_pb::id::RelationId;\n    use risingwave_sqlparser::ast::ObjectName;\n\n    use crate::Binder;\n    use crate::catalog::CatalogError;\n    use crate::catalog::root_catalog::SchemaPath;\n    use crate::catalog::schema_catalog::SchemaCatalog;\n    use crate::error::Result;\n    use crate::session::SessionImpl;\n","sourceCodeStart":323,"sourceCodeEnd":359,"githubUrl":"https://github.com/risingwavelabs/risingwave/blob/6469eb736d691e8e9b8a419a57edd6429ca77417/src/frontend/src/optimizer/backfill_order_strategy.rs#L323-L359","documentation":"The user-supplied backfill_order pairs are turned into a directed graph over scanned relation ids; plan_fixed_strategy runs has_cycle on the resulting order. A cycle (e.g. A->B, B->A) makes a total backfill ordering impossible, so creation is rejected.","triggerScenarios":"Specifying backfill_order pairs whose dependency edges form a loop, such as 'a -> b' together with 'b -> a', directly or transitively across multiple pairs.","commonSituations":"Conflicting ordering statements across several pairs, misunderstanding the arrow direction (pairs are not symmetric), or copy-pasting orders that contradict each other.","solutions":["Draw the dependency graph implied by your pairs and remove the contradictory edge(s).","Delete duplicate/redundant pairs that imply both directions between two tables.","Ensure the order is a strict topological order of the query's relations.","Omit backfill_order and let RisingWave choose an order if no strict ordering is needed."],"exampleFix":"// before\nWITH (backfill_order = 'a -> b, b -> a')\n// after\nWITH (backfill_order = 'a -> b')","handlingStrategy":"validation","validationCode":"-- pairs must form a DAG; check for both-direction pairs before DDL\n-- bad: backfill_order = 'a -> b, b -> a'","typeGuard":null,"tryCatchPattern":"match create_mv_result {\n    Err(e) if e.to_string().contains(\"has a cycle\") => {\n        eprintln!(\"backfill_order edges form a cycle; remove contradictory pairs\");\n    }\n    other => other?,\n}","preventionTips":["Sketch the ordering as a diagram and confirm it is acyclic before writing DDL.","Never emit both 'a -> b' and 'b -> a'.","Validate ordering clauses in CI with a small script that checks for cycles."],"tags":["sql","backfill","cycle","validation"],"backgroundTag":"invalid-argument-value","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"}