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
- Close and re-declare the cursor after re-checking the table exists (re-run the query with DECLARE).
- Verify the table/MV still exists: SELECT * FROM rw_catalog.rw_tables WHERE table_id = <dependent_table_id>.
- Avoid dropping tables that have open cursors; check active cursors before DDL.
- 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
- Close cursors before dropping their source tables/MVs.
- Keep cursor lifetimes short; avoid holding cursors across DDL migrations.
- Don't pin cursors to old query_epoch values during schema changes.
- After failover, re-validate cursor dependencies before FETCH.
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
- table id {dependent_table_id} has been dropped
- multiple clean watermark columns are not supported yet
- Filter can only receive bool array
- Exchange executor should not have children!
- Failed to execute time travel query
AI-assisted analysis of risingwavelabs/risingwave@6469eb736d (2026-09-11).
Data as JSON: /api/errors/27086060a14efa16.
Report an issue: GitHub.