{"record":{"id":"54ac6e6f1a026844","repo":"risingwavelabs/risingwave","slug":"iceberg-pk-index-sink-report-has-invalid-role","errorCode":null,"errorMessage":"iceberg pk-index sink report has invalid role: {}","messagePattern":"iceberg pk-index sink report has invalid role: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/meta/src/manager/iceberg_pk_index_sink/coordinator.rs","lineNumber":580,"sourceCode":"\n    let mut data_files: Vec<SerializedDataFile> = Vec::new();\n    let mut delete_files: Vec<SerializedDataFile> = Vec::new();\n    let mut overwrite_files: Vec<SerializedDataFile> = Vec::new();\n\n    if reports.is_empty() {\n        bail!(\"no reports to aggregate for iceberg pk-index sink coordinator\");\n    }\n\n    for r in reports {\n        let Some(meta) = &r.metadata else {\n            bail!(\"iceberg pk-index sink report missing metadata in aggregate_reports\");\n        };\n\n        // Validate role: explicitly-Unspecified is a wire-format bug.\n        let role = PbIcebergPkIndexSinkRole::try_from(r.role)\n            .ok()\n            .filter(|r| !matches!(r, PbIcebergPkIndexSinkRole::Unspecified))\n            .ok_or_else(|| anyhow!(\"iceberg pk-index sink report has invalid role: {}\", r.role))?;\n\n        match role {\n            PbIcebergPkIndexSinkRole::Writer => {\n                let commit_result = IcebergCommitResult::try_from(meta)?;\n                align_report_id(\n                    commit_result.schema_id,\n                    commit_result.partition_spec_id,\n                    &mut shared_schema_id,\n                    &mut shared_partition_spec_id,\n                )?;\n                data_files.extend(commit_result.data_files);\n            }\n            PbIcebergPkIndexSinkRole::PositionDeleteMerger => {\n                let commit_result =\n                    IcebergPositionDeleteCommitResult::try_from(meta).map_err(|e| {\n                        anyhow!(e).context(\"decode pk-index sink position-delete merger metadata\")\n                    })?;\n                align_report_id(","sourceCodeStart":562,"sourceCodeEnd":598,"githubUrl":"https://github.com/risingwavelabs/risingwave/blob/6469eb736d691e8e9b8a419a57edd6429ca77417/src/meta/src/manager/iceberg_pk_index_sink/coordinator.rs#L562-L598","documentation":"During commit coordination, the meta service aggregates status reports from all workers participating in an Iceberg pk-index sink. Each report carries a protobuf enum `role` that must be Writer or PositionDeleteMerger; if the raw i32 does not map to a valid variant, or maps to `Unspecified`, the wire format is corrupt and `aggregate_reports` aborts the commit with this anyhow error.","triggerScenarios":"IcebergPkIndexSinkCoordinator::pre_commit_epoch -> aggregate_reports iterates worker reports and calls PbIcebergPkIndexSinkRole::try_from(r.role); it fails when a report's role field is 0 (Unspecified, the protobuf default) or any out-of-range i32 value.","commonSituations":"Version skew between meta and worker binaries so the role field is never set; hand-crafted or replayed report protobufs; a worker bug that forgets to assign the role before sending its commit report.","solutions":["Upgrade all worker nodes to the same version as the meta node so the role field is always populated.","Fix the worker code that builds the sink report to explicitly set role to Writer or PositionDeleteMerger.","Log/inspect the report's raw role value to identify the offending node and invalid value.","Restart the offending worker and retry the sink commit."],"exampleFix":"// before (worker builds report with default role)\nlet report = PbIcebergPkIndexSinkReport { report_meta: Some(meta), ..Default::default() };\n// after\nlet report = PbIcebergPkIndexSinkReport {\n    role: PbIcebergPkIndexSinkRole::Writer as i32,\n    report_meta: Some(meta),\n    ..Default::default()\n};","handlingStrategy":"validation","validationCode":"// worker-side, before sending the report\nlet role = PbIcebergPkIndexSinkRole::try_from(report.role)\n    .ok()\n    .filter(|r| *r != PbIcebergPkIndexSinkRole::Unspecified)\n    .ok_or_else(|| anyhow!(\"report.role not set: {}\", report.role))?;","typeGuard":"fn is_valid_role(v: i32) -> bool {\n    matches!(\n        PbIcebergPkIndexSinkRole::try_from(v),\n        Ok(PbIcebergPkIndexSinkRole::Writer) | Ok(PbIcebergPkIndexSinkRole::PositionDeleteMerger)\n    )\n}","tryCatchPattern":null,"preventionTips":["Always set the role field explicitly when constructing sink reports; never rely on protobuf defaults.","Keep worker and meta binaries on the same version.","Unit-test report construction on the worker side.","Log the raw role value on any rejection to speed diagnosis."],"tags":["protobuf","iceberg","internal-error"],"backgroundTag":"invalid-enum-value","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"}