{"record":{"id":"7313864b2463a7b7","repo":"risingwavelabs/risingwave","slug":"batch-refresh-materialized-view-must-have-snapshot","errorCode":null,"errorMessage":"batch refresh materialized view must have snapshot backfill upstream","messagePattern":"batch refresh materialized view must have snapshot backfill upstream","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/meta/src/stream/stream_manager.rs","lineNumber":674,"sourceCode":"            streaming_job: streaming_job.clone(),\n            job_type,\n            create_type,\n            database_resource_group,\n            fragment_backfill_ordering,\n            cdc_table_snapshot_splits,\n            locality_fragment_state_table_mapping,\n            is_serverless: is_serverless_backfill,\n            streaming_job_model,\n            replace_sink,\n            refresh_interval_sec,\n        };\n\n        let job_type = if let Some(refresh_interval_sec) = refresh_interval_sec {\n            if since_timestamp_epoch.is_some() {\n                bail!(\"since_timestamp should not be specified when no snapshot backfill\");\n            }\n            let snapshot_backfill_info = snapshot_backfill_info.ok_or_else(|| {\n                anyhow::anyhow!(\n                    \"batch refresh materialized view must have snapshot backfill upstream\"\n                )\n            })?;\n            // Batch refresh jobs must not contain source or source-backfill nodes,\n            // because we skip split assignment resolution for them.\n            for fragment in info.stream_job_fragments.inner.fragments.values() {\n                let mask = fragment.fragment_type_mask;\n                if mask.contains(FragmentTypeFlag::Source)\n                    || mask.contains(FragmentTypeFlag::SourceScan)\n                {\n                    bail!(\n                        \"batch refresh materialized views must not depend on sources directly; \\\n                         fragment {} has source/source-backfill nodes\",\n                        fragment.fragment_id\n                    );\n                }\n            }\n            tracing::debug!(","sourceCodeStart":656,"sourceCodeEnd":692,"githubUrl":"https://github.com/risingwavelabs/risingwave/blob/6469eb736d691e8e9b8a419a57edd6429ca77417/src/meta/src/stream/stream_manager.rs#L656-L692","documentation":"A batch refresh materialized view (refresh_interval_sec specified) must have a snapshot backfill upstream, but snapshot_backfill_info was None. Batch refresh relies on the snapshot backfill machinery to obtain the base data, so without it the job cannot be constructed and the meta node bails.","triggerScenarios":"Creating a streaming job with refresh_interval_sec set while snapshot_backfill_info is None — e.g. the MV's upstream was not created as a snapshot backfill MV, or the info was not propagated through the command.","commonSituations":"Declaring an interval-refresh MV on a regular table or non-backfill MV; upgrading from a version without snapshot backfill where upstream MVs lack backfill metadata; manually crafting gRPC create commands omitting snapshot_backfill_info.","solutions":["Ensure the upstream materialized view was created with snapshot backfill support before creating the batch refresh MV.","If batch refresh is not actually needed, create a normal (streaming) MV instead of specifying refresh_interval_sec.","Recreate the upstream MV with the snapshot backfill option, then retry the create."],"exampleFix":"// before: upstream MV created without snapshot backfill, then:\nCREATE MATERIALIZED VIEW mv WITH (refresh = '1 hour') AS SELECT * FROM upstream;\n-- after: create upstream with snapshot backfill first, or drop refresh option\nCREATE MATERIALIZED VIEW upstream WITH (backfill = true) AS ...;\nCREATE MATERIALIZED VIEW mv WITH (refresh = '1 hour') AS SELECT * FROM upstream;","handlingStrategy":"validation","validationCode":"-- verify upstream supports snapshot backfill before creating a batch refresh MV\nSELECT name FROM rw_materialized_views WHERE name = 'upstream' AND properties LIKE '%backfill%';","typeGuard":null,"tryCatchPattern":"match result {\n    Err(e) if e.to_string().contains(\"must have snapshot backfill upstream\") =>\n        return Err(\"recreate upstream with snapshot backfill first\".into()),\n    other => other,\n}","preventionTips":["Create upstream MVs with snapshot backfill enabled when downstream batch refresh is planned.","Do not hand-craft create commands omitting snapshot_backfill_info.","Standardize MV creation through reviewed SQL templates."],"tags":["sql","validation","materialized-view","snapshot-backfill"],"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"}