risingwavelabs/risingwave · error
Creating such a sink will result in circular dependency.
Error message
Creating such a sink will result in circular dependency.
What it means
Creating a sink whose target table would create a cycle of dependencies among relations (the sink depends on a table that transitively depends on the sink) is forbidden. The catalog dependency check found a cycle before inserting the sink catalog.
Solutions
- Redesign the pipeline to break the cycle (split into separate tables or an external sink).
- Drop one of the intermediate relations creating the cycle.
- Create the sink into a fresh table not read by its own upstream chain.
- Validate the dependency graph manually (rw_dependencies / upstream queries) before creating the sink.
Defensive patterns
Strategy: validation
Validate before calling
// before creating the sink, ensure the target table is not read
// (transitively) by the sink's source query
let cycle = check_relation_cycle(target_table_id, dependencies, &txn).await?;
if cycle { /* abort creation */ } Try / catch
match create_sink_catalog(..).await {
Err(e) if e.to_string().contains("circular dependency") => /* redesign pipeline; drop cycle link */,
other => other?,
} Prevention
- Model pipelines as a DAG before creating sink-into-table
- Check rw_dependencies before wiring sinks into tables read by the sink's sources
When it happens
Trigger: create_job_catalog for a sink whose target_table participates in an existing dependency cycle with the sink's upstream relations, detected by the dependency check over the txn.
Common situations: Users wiring sinks into tables that feed other MVs which the sink's source query reads (A -> B -> A pipelines), often from incremental schema evolution.
Understand the failure class
Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.
Related errors
- Dropping sink into table is not allowed for unmigrated table
- duplicate vnode . request vnode: , prev vnode: . pending…
- expected exactly one sink fragment for each sink, but got
- Hummock committed epoch sender closed unexpectedly
- no state table id in sink
AI-assisted analysis of risingwavelabs/risingwave@6469eb736d (2026-09-11).
Data as JSON: /api/errors/1b4ccb42228b9e95.
Report an issue: GitHub.
Appendix: source
Thrown at src/meta/src/controller/streaming_job.rs:623
backfill_adaptive_parallelism_strategy,
refresh_interval_sec,
)
.await?;
table.id = streaming_job_model.job_id.as_mv_table_id();
let table_model: table::ActiveModel = table.clone().into();
Table::insert(table_model).exec(&txn).await?;
streaming_job_model
}
StreamingJob::Sink(sink, belong_to_table_id) => {
if let Some(target_table_id) = sink.target_table
&& check_sink_into_table_cycle(
target_table_id.into(),
dependencies.iter().cloned().collect(),
&txn,
)
.await?
{
bail!("Creating such a sink will result in circular dependency.");
}
let streaming_job_model = Self::create_streaming_job_obj(
&txn,
ObjectType::Sink,
sink.owner as _,
Some(
belong_to_table_id
.map(|table_id| table_id.as_object_id())
.unwrap_or(sink.schema_id.as_object_id()),
),
create_type,
ctx.clone(),
adaptive_parallelism_strategy,
streaming_parallelism,
max_parallelism,
resource_type.clone(),
backfill_parallelism.clone(),View on GitHub (pinned to 6469eb736d)