risingwavelabs/risingwave · error

dependent_table_id {dependent_table_id} not exists

Error message

dependent_table_id {dependent_table_id} not exists

What it means

RisingWave's cursor manager resolves the snapshot a FETCH/CURSOR depends on by looking up the dependent table id in the snapshot's state_table_info. This error means the table id backing the cursor is absent from the recorded snapshot metadata, so the cursor cannot be advanced from the requested epoch. It is thrown when internal bookkeeping between the catalog and snapshot versions is inconsistent, typically because the underlying table was dropped or the snapshot is stale.

Source

Thrown at src/frontend/src/session/cursor_manager.rs:456

            )
        } else {
            // The query stream needs to initiated on cursor creation to make sure
            // future fetch on the cursor starts from the snapshot when the cursor is declared.
            //
            // TODO: is this the right behavior? Should we delay the query stream initiation till the first fetch?
            let (chunk_stream, init_query_timer, table_catalog) =
                Self::initiate_query(None, dependent_table_id, handler_args.clone(), None).await?;
            let pinned_epoch = match handler_args.session.get_pinned_snapshot().ok_or_else(
                || ErrorCode::InternalError("Fetch Cursor can't find snapshot epoch".to_owned()),
            )? {
                ReadSnapshot::FrontendPinned { snapshot, .. } => {
                    snapshot
                        .version()
                        .state_table_info
                        .info()
                        .get(&dependent_table_id)
                        .ok_or_else(|| {
                            anyhow!("dependent_table_id {dependent_table_id} not exists")
                        })?
                        .committed_epoch
                }
                ReadSnapshot::Other(_) => {
                    return Err(ErrorCode::InternalError("Fetch Cursor can't start from specified query epoch. May run `set query_epoch = 0;`".to_owned()).into());
                }
                ReadSnapshot::ReadUncommitted => {
                    return Err(ErrorCode::InternalError(
                        "Fetch Cursor don't support read uncommitted".to_owned(),
                    )
                    .into());
                }
            };
            let start_timestamp = pinned_epoch;

            (
                State::Fetch {
                    from_snapshot: true,

View on GitHub (pinned to 6469eb736d)

Solutions

  1. Close and re-declare the cursor after re-checking the table exists (re-run the query with DECLARE).
  2. Verify the table/MV still exists: SELECT * FROM rw_catalog.rw_tables WHERE table_id = <dependent_table_id>.
  3. Avoid dropping tables that have open cursors; check active cursors before DDL.
  4. If a custom query_epoch was set, unset it (SET query_epoch = 0) and retry.

Example fix

// before
DECLARE c CURSOR FOR SELECT * FROM my_mv;
DROP MATERIALIZED VIEW my_mv;
FETCH 10 FROM c; -- error

// after
DECLARE c CURSOR FOR SELECT * FROM my_mv;
FETCH 10 FROM c;
CLOSE c;
DROP MATERIALIZED VIEW my_mv;
Defensive patterns

Strategy: try-catch

Validate before calling

-- before resuming a cursor
SELECT count(*) FROM rw_catalog.rw_tables WHERE name = 'my_mv';
-- if 0, re-declare instead of fetching

Try / catch

match session.declare_or_fetch_cursor(...) {
    Ok(rows) => rows,
    Err(e) if e.to_string().contains("dependent_table_id") => {
        // table dropped: re-declare cursor
        redeclare_cursor();
    }
    Err(e) => return Err(e),
}

Prevention

When it happens

Trigger: Calling new() to create/advance a cursor whose dependent_table_id is no longer present in snapshot.version().state_table_info; e.g. the table (or materialized view) the cursor reads was dropped after the cursor was declared, or a cursor pinned to an old snapshot/epoch is resumed after a schema or catalog change.

Common situations: Long-lived cursors held open across DDL; a session dropping the MV/table while a cursor still references it; snapshot retention/GC evicting the epoch the cursor points at; recovery after failover where snapshot metadata no longer contains the table.

Understand the failure class

Background: Record Not Found Errors: "not found", RecordNotFound, and "was not found" — what they mean and how to fix them — this error's family across 28 libraries.

Related errors


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