{"record":{"id":"9a09ec4e9ce93ba9","repo":"risingwavelabs/risingwave","slug":"connector-name-snapshot-primary-key-cannot","errorCode":null,"errorMessage":"{connector_name} snapshot primary key `{}` cannot be NULL","messagePattern":"(.+?) snapshot primary key `(.+?)` cannot be NULL","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/connector/src/parser/mod.rs","lineNumber":108,"sourceCode":"        let decode_result = decode(index, field);\n        let datum = if is_pk {\n            decode_result.with_context(|| {\n                format!(\n                    \"failed to decode {connector_name} snapshot primary key `{}`\",\n                    field.name\n                )\n            })?\n        } else {\n            match decode_result {\n                Ok(datum) => datum,\n                Err(err) => {\n                    log_non_pk_error(&field.name, err);\n                    None\n                }\n            }\n        };\n        if is_pk && datum.is_none() {\n            bail!(\n                \"{connector_name} snapshot primary key `{}` cannot be NULL\",\n                field.name\n            );\n        }\n        datums.push(datum);\n    }\n    Ok(OwnedRow::new(datums))\n}\n\n#[cfg(test)]\nmod strict_pk_tests {\n    use anyhow::anyhow;\n    use risingwave_common::row::Row;\n    use risingwave_common::types::{DataType, ScalarImpl};\n\n    use super::*;\n\n    fn test_schema() -> Schema {","sourceCodeStart":90,"sourceCodeEnd":126,"githubUrl":"https://github.com/risingwavelabs/risingwave/blob/6469eb736d691e8e9b8a419a57edd6429ca77417/src/connector/src/parser/mod.rs#L90-L126","documentation":"In strict primary-key decoding mode, `decode_row_with_strict_pk` decodes each field; non-PK decode failures are logged and yield NULL (lenient), but a NULL value for a primary-key column is fatal. This preserves the invariant that every decoded snapshot row has a non-NULL primary key, since RW rows must be keyed.","triggerScenarios":"A CDC snapshot record whose decoded primary-key field is absent, unparseable (non-PK-style decode produced None), or explicitly NULL is passed to `decode_row_with_strict_pk`.","commonSituations":"Upstream table has a nullable column used as RW primary key; the CDC payload omits the pk field; a decode failure on the pk column (type mismatch, encoding issue) silently produces None which then trips this check; schema drift so the value lands in the wrong column.","solutions":["Fix the data: backfill or exclude NULL primary keys in the upstream table before snapshotting.","Choose a non-nullable column (or composite key) as the RW primary key and update the table definition.","Investigate why the pk field decoded to None (check preceding decode-error logs from `log_non_pk_error` and the message schema) and fix the encoding/schema mapping."],"exampleFix":"-- before: nullable column as pk\nCREATE TABLE t (id INT PRIMARY KEY, ...); -- id is NULL in upstream\n-- after: use a non-nullable key\nCREATE TABLE t (uuid VARCHAR PRIMARY KEY, id INT, ...);","handlingStrategy":"validation","validationCode":"if pk_columns.iter().any(|c| c.nullable) {\n    return Err(anyhow!(\"primary key columns must be non-nullable for strict pk decoding\"));\n}","typeGuard":null,"tryCatchPattern":"match decode_row_with_strict_pk(...) {\n    Err(e) if e.to_string().contains(\"cannot be NULL\") => {\n        log::warn!(\"snapshot row with NULL pk skipped/quarantined: {e}\");\n    }\n    other => other?,\n}","preventionTips":["Only choose NOT NULL columns (or add a surrogate key) as RW primary keys for CDC tables.","Check for prior decode-error logs (log_non_pk_error) to distinguish a decode failure from a true NULL.","Validate upstream data for NULL keys before starting a snapshot."],"tags":["cdc","snapshot","primary-key","null","schema"],"backgroundTag":"null-argument","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"}