{"record":{"id":"530b634e572348dd","repo":"risingwavelabs/risingwave","slug":"table-reader-closed","errorCode":null,"errorMessage":"table reader closed","messagePattern":"table reader closed","errorType":"exception","errorClass":"DmlError","httpStatus":null,"severity":"error","filePath":"src/dml/src/error.rs","lineNumber":26,"sourceCode":"//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n/// The error type for DML operations.\n#[derive(thiserror::Error, Debug)]\npub enum DmlError {\n    #[error(\"table schema has changed, please try again later\")]\n    SchemaChanged,\n\n    #[error(\n        \"DML is not permitted during cluster recovery (no available table reader in streaming executors)\"\n    )]\n    NoReader,\n\n    #[error(\"table reader closed\")]\n    ReaderClosed,\n}\n\npub type Result<T> = std::result::Result<T, DmlError>;\n","sourceCodeStart":8,"sourceCodeEnd":31,"githubUrl":"https://github.com/risingwavelabs/risingwave/blob/6469eb736d691e8e9b8a419a57edd6429ca77417/src/dml/src/error.rs#L8-L31","documentation":"DmlError::ReaderClosed indicates the table reader associated with a DML operation has been closed — the channel from the streaming executors to the DML path was dropped, typically because the streaming actor backing the table was stopped, restarted, or the table was dropped.","triggerScenarios":"A write is issued through a table reader whose streaming executor has terminated (actor restart, table drop, cluster shutdown) — the read channel is closed and further DML through it fails.","commonSituations":"Ingestion client holding a long-lived reader while the MV/table is dropped elsewhere; worker restarts under the client; shutting down the cluster while writes are still being submitted.","solutions":["Re-establish the write path: recreate the DML session/statement so a fresh table reader is acquired.","Verify the target table still exists and the streaming actors are running.","Treat it as transient during restarts and retry with backoff after the cluster is healthy."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"// verify table exists and cluster is up before reusing a long-lived DML reader","typeGuard":null,"tryCatchPattern":"try { writeThroughReader(reader); }\ncatch (e) {\n  if (String(e).includes(\"table reader closed\")) { reader = await reopenReader(tableId); writeThroughReader(reader); }\n  else throw e;\n}","preventionTips":["Do not cache table readers across table drops or cluster restarts","Detect closed channels and recreate the DML session","Avoid writing during shutdown sequences"],"tags":["dml","reader","channel-closed"],"backgroundTag":"invalid-state-transition","analyzedSha":"6469eb736d691e8e9b8a419a57edd6429ca77417","analyzedAt":"2026-09-11T21:06:21.487Z","contentChangedAt":"2026-09-11T21:06:21.487Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}