{"record":{"id":"27086060a14efa16","repo":"risingwavelabs/risingwave","slug":"dependent-table-id-dependent-table-id-not-exists","errorCode":null,"errorMessage":"dependent_table_id {dependent_table_id} not exists","messagePattern":"dependent_table_id (.+?) not exists","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/frontend/src/session/cursor_manager.rs","lineNumber":456,"sourceCode":"            )\n        } else {\n            // The query stream needs to initiated on cursor creation to make sure\n            // future fetch on the cursor starts from the snapshot when the cursor is declared.\n            //\n            // TODO: is this the right behavior? Should we delay the query stream initiation till the first fetch?\n            let (chunk_stream, init_query_timer, table_catalog) =\n                Self::initiate_query(None, dependent_table_id, handler_args.clone(), None).await?;\n            let pinned_epoch = match handler_args.session.get_pinned_snapshot().ok_or_else(\n                || ErrorCode::InternalError(\"Fetch Cursor can't find snapshot epoch\".to_owned()),\n            )? {\n                ReadSnapshot::FrontendPinned { snapshot, .. } => {\n                    snapshot\n                        .version()\n                        .state_table_info\n                        .info()\n                        .get(&dependent_table_id)\n                        .ok_or_else(|| {\n                            anyhow!(\"dependent_table_id {dependent_table_id} not exists\")\n                        })?\n                        .committed_epoch\n                }\n                ReadSnapshot::Other(_) => {\n                    return Err(ErrorCode::InternalError(\"Fetch Cursor can't start from specified query epoch. May run `set query_epoch = 0;`\".to_owned()).into());\n                }\n                ReadSnapshot::ReadUncommitted => {\n                    return Err(ErrorCode::InternalError(\n                        \"Fetch Cursor don't support read uncommitted\".to_owned(),\n                    )\n                    .into());\n                }\n            };\n            let start_timestamp = pinned_epoch;\n\n            (\n                State::Fetch {\n                    from_snapshot: true,","sourceCodeStart":438,"sourceCodeEnd":474,"githubUrl":"https://github.com/risingwavelabs/risingwave/blob/6469eb736d691e8e9b8a419a57edd6429ca77417/src/frontend/src/session/cursor_manager.rs#L438-L474","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\nDECLARE c CURSOR FOR SELECT * FROM my_mv;\nDROP MATERIALIZED VIEW my_mv;\nFETCH 10 FROM c; -- error\n\n// after\nDECLARE c CURSOR FOR SELECT * FROM my_mv;\nFETCH 10 FROM c;\nCLOSE c;\nDROP MATERIALIZED VIEW my_mv;","handlingStrategy":"try-catch","validationCode":"-- before resuming a cursor\nSELECT count(*) FROM rw_catalog.rw_tables WHERE name = 'my_mv';\n-- if 0, re-declare instead of fetching","typeGuard":null,"tryCatchPattern":"match session.declare_or_fetch_cursor(...) {\n    Ok(rows) => rows,\n    Err(e) if e.to_string().contains(\"dependent_table_id\") => {\n        // table dropped: re-declare cursor\n        redeclare_cursor();\n    }\n    Err(e) => return Err(e),\n}","preventionTips":["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."],"tags":["cursor","snapshot","catalog","streaming"],"backgroundTag":"record-not-found","analyzedSha":"6469eb736d691e8e9b8a419a57edd6429ca77417","analyzedAt":"2026-09-11T21:06:21.487Z","contentChangedAt":"2026-09-11T21:06:21.487Z","schemaVersion":2},"datasetVersion":"2026-09-14T11:17:12.474Z"}