{"record":{"id":"821f7c0025b92aa8","repo":"risingwavelabs/risingwave","slug":"no-chunk-in-ingestdmlpayloadrequest","errorCode":null,"errorMessage":"no chunk in IngestDmlPayloadRequest","messagePattern":"no chunk in IngestDmlPayloadRequest","errorType":"error_code","errorClass":"BatchError","httpStatus":null,"severity":"error","filePath":"src/batch/src/rpc/service/task_service.rs","lineNumber":338,"sourceCode":"        let table_version_id = init.table_version_id;\n        let table_dml_handle = self\n            .env\n            .dml_manager_ref()\n            .table_dml_handle(table_id, table_version_id)\n            .map_err(|err| Status::internal(format!(\"{}\", err.as_report())))?;\n        Ok((table_dml_handle, init.request_id, init.row_id_index))\n    }\n\n    async fn do_ingest_dml_payload(\n        table_dml_handle: TableDmlHandleRef,\n        dml_manager: DmlManagerRef,\n        request_id: u32,\n        row_id_index: Option<u32>,\n        payload: IngestDmlPayloadRequest,\n    ) -> Result<impl Future<Output = risingwave_dml::error::Result<()>> + Send + 'static, BatchError>\n    {\n        let pb_chunk = payload.chunk.ok_or_else(|| {\n            BatchError::Internal(anyhow::anyhow!(\"no chunk in IngestDmlPayloadRequest\"))\n        })?;\n        let mut chunk = StreamChunk::from_protobuf(&pb_chunk)\n            .context(\"failed to decode chunk\")\n            .map_err(BatchError::Internal)?;\n        chunk = inject_optional_row_id_column(chunk, row_id_index.map(|index| index as usize));\n        let txn_id = dml_manager.gen_txn_id();\n        let mut write_handle = table_dml_handle\n            .write_handle(request_id, txn_id)\n            .map_err(BatchError::Dml)?;\n\n        write_handle.begin().map_err(BatchError::Dml)?;\n        write_handle\n            .write_chunk(chunk)\n            .await\n            .map_err(BatchError::Dml)?;\n        let persistence_future = write_handle\n            .end_wait_persistence()\n            .map_err(BatchError::Dml)?;","sourceCodeStart":320,"sourceCodeEnd":356,"githubUrl":"https://github.com/risingwavelabs/risingwave/blob/6469eb736d691e8e9b8a419a57edd6429ca77417/src/batch/src/rpc/service/task_service.rs#L320-L356","documentation":"Raised in do_ingest_dml_payload when an IngestDmlPayloadRequest arrives over the batch/DML gRPC service without a `chunk` field. The handler requires exactly one data chunk to decode into a StreamChunk; a request without one is an internal protocol violation and is reported as BatchError::Internal with this message.","triggerScenarios":"A client (or older/newer SDK) calls the ingest DML payload RPC (e.g. from the PostgreSQL wire-protocol DML path) but sends the request with `chunk = None` — e.g. an empty batch, a truncated write, or a client that skips chunk serialization when there are no rows.","commonSituations":"Writing zero rows via a driver that still issues an ingest call, version skew between frontend and client protocol handling, custom tooling built against the DML gRPC API omitting the chunk field.","solutions":["Fix the caller to always populate `chunk` before sending IngestDmlPayloadRequest.","Skip issuing the ingest RPC entirely when there are no rows to write.","Check client/SDK version skew against the cluster's protobuf definitions and upgrade.","If it comes from a driver, capture the request with logging and report/inspect why the chunk was empty."],"exampleFix":"// before\n// client sends payload even for empty batch\nsend(IngestDmlPayloadRequest { request_id, chunk: None });\n\n// after\nif !rows.is_empty() {\n    send(IngestDmlPayloadRequest { request_id, chunk: Some(to_protobuf_chunk(rows)) });\n}","handlingStrategy":"validation","validationCode":"// Client-side guard before sending the RPC\nif payload.chunk.is_none() {\n    return; // skip ingest call entirely for empty batches\n}","typeGuard":null,"tryCatchPattern":"match do_ingest_dml_payload(...).await {\n    Err(BatchError::Internal(msg)) if msg.contains(\"no chunk in IngestDmlPayloadRequest\") => {\n        // treat as empty batch: skip or re-send with a valid chunk\n    }\n    other => other?,\n}","preventionTips":["Never send IngestDmlPayloadRequest without Some(chunk); skip the RPC for empty writes.","Keep client SDK and cluster protobuf definitions in version sync.","Add client-side assertion that serialized DML batches contain at least one row or are dropped."],"tags":["grpc","dml","protobuf","internal"],"backgroundTag":"empty-required-field","analyzedSha":"6469eb736d691e8e9b8a419a57edd6429ca77417","analyzedAt":"2026-09-11T21:06:21.487Z","contentChangedAt":"2026-09-11T21:06:21.487Z","schemaVersion":2},"datasetVersion":"2026-09-14T16:17:12.679Z"}