risingwavelabs/risingwave · error · MetaError

replace sink must not use snapshot backfill

Error message

replace sink must not use snapshot backfill

What it means

A sink replace (ReplaceSink) command attempted to replace an existing sink using a job type of SnapshotBackfill or BatchRefresh, which is unsupported for replacement. Sink replacement must be a plain sink job, so the applier bails out.

Source

Thrown at src/meta/src/barrier/checkpoint/state.rs:910

                let resolved_split_assignment = resolve_source_splits(
                    &info,
                    &actors,
                    edges.actor_new_no_shuffle(),
                    &self.database_info,
                )?;

                let old_sink_job_id = info
                    .replace_sink
                    .as_ref()
                    .map(|old_sink_id| old_sink_id.as_job_id());
                if old_sink_job_id.is_some()
                    && matches!(
                        job_type,
                        CreateStreamingJobType::SnapshotBackfill { .. }
                            | CreateStreamingJobType::BatchRefresh(_)
                    )
                {
                    bail!("replace sink must not use snapshot backfill");
                }

                // Pre-apply: add new job and fragments
                let cdc_tracker = if let Some(splits) = &info.cdc_table_snapshot_splits {
                    let (fragment, _) =
                        parallel_cdc_table_backfill_fragment(info.stream_job_fragments.fragments())
                            .expect("should have parallel cdc fragment");
                    Some(CdcTableBackfillTracker::new(
                        fragment.fragment_id,
                        splits.clone(),
                    ))
                } else {
                    None
                };
                self.database_info
                    .pre_apply_new_job(info.streaming_job.id(), cdc_tracker);
                self.database_info.pre_apply_new_fragments(
                    info.stream_job_fragments

View on GitHub (pinned to 6469eb736d)

Solutions

  1. Replace the sink without snapshot backfill/batch refresh job type
  2. Drop the old sink and create the new one as a fresh job instead
  3. Remove backfill options from the replace statement

Example fix

-- before: replace with backfill
ALTER SINK s AS ... WITH BACKFILL;
-- after
DROP SINK s;
CREATE SINK s AS ... WITH BACKFILL;
Defensive patterns

Strategy: validation

Validate before calling

if is_replace_sink(job) && matches!(job.job_type, SnapshotBackfill{..} | BatchRefresh(_)) {
    return Err("use DROP+CREATE for backfill sinks");
}

Type guard

fn replace_allows_backfill(jt: &CreateStreamingJobType) -> bool {
    !matches!(jt, CreateStreamingJobType::SnapshotBackfill{..} | CreateStreamingJobType::BatchRefresh(_))
}

Prevention

When it happens

Trigger: Applying a replace-sink command via handle_new_barrier where the new job's CreateStreamingJobType is SnapshotBackfill or BatchRefresh.

Common situations: User tries to recreate a sink with a backfill/snapshot clause while an existing sink is being replaced; tooling or migration scripts generate replace commands with backfill options.

Understand the failure class

Background: UnsupportedOperationException and "is not supported" errors: when a library deliberately refuses a call — this error's family across 30 libraries.

Related errors


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