{"record":{"id":"c8afee491c393c04","repo":"risingwavelabs/risingwave","slug":"sql-server-max-lsn-is-null","errorCode":null,"errorMessage":"SQL Server max_lsn is NULL","messagePattern":"SQL Server max_lsn is NULL","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/connector/src/source/cdc/enumerator/mod.rs","lineNumber":348,"sourceCode":"            for byte in &bytes[0..4] {\n                hex_string.push_str(&format!(\"{:02x}\", byte));\n            }\n            hex_string.push(':');\n            for byte in &bytes[4..8] {\n                hex_string.push_str(&format!(\"{:02x}\", byte));\n            }\n            hex_string.push(':');\n            for byte in &bytes[8..10] {\n                hex_string.push_str(&format!(\"{:02x}\", byte));\n            }\n            Ok(hex_string)\n        };\n\n        let max_lsn = row\n            .try_get::<&[u8], usize>(0)?\n            .map(lsn_bytes_to_hex)\n            .transpose()?\n            .ok_or_else(|| anyhow!(\"SQL Server max_lsn is NULL\"))?;\n        let min_lsn = row\n            .try_get::<&[u8], usize>(1)?\n            .map(lsn_bytes_to_hex)\n            .transpose()?\n            .ok_or_else(|| anyhow!(\"SQL Server min_lsn is NULL\"))?;\n\n        Ok(Some((min_lsn, max_lsn)))\n    }\n\n    async fn monitor_sql_server_lsns(&mut self) -> ConnectorResult<()> {\n        let lsns = self.query_sql_server_lsns().await.with_context(|| {\n            format!(\n                \"failed to query SQL Server LSNs for source {}\",\n                self.source_id\n            )\n        })?;\n        if let Some((min_lsn, max_lsn)) = lsns {\n            let labels = vec![self.source_id.to_string()];","sourceCodeStart":330,"sourceCodeEnd":366,"githubUrl":"https://github.com/risingwavelabs/risingwave/blob/6469eb736d691e8e9b8a419a57edd6429ca77417/src/connector/src/source/cdc/enumerator/mod.rs#L330-L366","documentation":"After reading the row, the max_lsn column is Option<&[u8]>; if it is NULL (None), this error is thrown because a hex-encoded max LSN string is mandatory for CDC monitoring. `sys.fn_cdc_get_max_lsn` returns NULL when CDC is not enabled on the database.","triggerScenarios":"The LSN query returns a row whose first column (max_lsn from `sys.fn_cdc_get_max_lsn()`) is NULL — CDC metadata absent for the database.","commonSituations":"CDC enabled on the table's capture list but database-level CDC max LSN is uninitialized; SQL Server Agent capture job has never run; querying a restored/attached database where CDC jobs were not recreated.","solutions":["Ensure database-level CDC is enabled (`sys.sp_cdc_enable_db`) so `fn_cdc_get_max_lsn` returns a value","Start/verify SQL Server Agent and the `cdc.<db>_capture` job","Re-initialize CDC on restored databases (re-run sp_cdc_enable_db/table and jobs)","Confirm the query targets the correct database"],"exampleFix":"// ensure capture job runs\nEXEC sys.sp_cdc_start_job @job_type = N'capture';","handlingStrategy":"validation","validationCode":"// Precheck on SQL Server:\n// SELECT sys.fn_cdc_get_max_lsn() IS NULL AS max_lsn_missing;","typeGuard":null,"tryCatchPattern":"match res {\n    Err(e) if e.to_string().contains(\"max_lsn is NULL\") => {\n        warn!(\"CDC max LSN unavailable; ensure CDC and capture job are enabled\");\n    }\n    other => other?,\n}","preventionTips":["Run sp_cdc_enable_db and start the capture job before monitoring","Check that the capture job runs on a schedule (SQL Server Agent up)"],"tags":["cdc","sql-server","lsn","null-value"],"backgroundTag":"unexpected-response-shape","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"}