{"record":{"id":"f67d7f5480fa563c","repo":"risingwavelabs/risingwave","slug":"sql-server-lsn-should-be-10-bytes-got-bytes","errorCode":null,"errorMessage":"SQL Server LSN should be 10 bytes, got {} bytes","messagePattern":"SQL Server LSN should be 10 bytes, got (.+?) bytes","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/connector/src/source/cdc/enumerator/mod.rs","lineNumber":323,"sourceCode":"        config.trust_cert();\n\n        let mut client = SqlServerClient::new_with_config(config).await?;\n        let row = client\n            .inner_client\n            .simple_query(\n                \"SELECT \\\n                    sys.fn_cdc_get_max_lsn() AS max_lsn, \\\n                    (SELECT MIN(sys.fn_cdc_get_min_lsn(capture_instance)) FROM cdc.change_tables) AS min_lsn\"\n                    .to_owned(),\n            )\n            .await?\n            .into_row()\n            .await?\n            .ok_or_else(|| anyhow!(\"No result returned when querying SQL Server max/min LSN\"))?;\n\n        let lsn_bytes_to_hex = |bytes: &[u8]| -> ConnectorResult<String> {\n            if bytes.len() != 10 {\n                return Err(anyhow!(\n                    \"SQL Server LSN should be 10 bytes, got {} bytes\",\n                    bytes.len()\n                )\n                .into());\n            }\n            let mut hex_string = String::with_capacity(22);\n            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)","sourceCodeStart":305,"sourceCodeEnd":341,"githubUrl":"https://github.com/risingwavelabs/risingwave/blob/6469eb736d691e8e9b8a419a57edd6429ca77417/src/connector/src/source/cdc/enumerator/mod.rs#L305-L341","documentation":"The `lsn_bytes_to_hex` closure validates that a SQL Server LSN is exactly 10 bytes before hex-encoding it. A differently sized byte array indicates the column did not contain a binary(10) LSN as expected, so the error is thrown with the actual byte count.","triggerScenarios":"The row returned by the LSN query contains a value at the max_lsn/min_lsn column whose length differs from 10 bytes — e.g. the query result shape changed, or a NULL-adjacent/wrong column was bound.","commonSituations":"SQL Server version differences in `sys.fn_cdc_get_max_lsn` output binding; tiberius returning the value as a different binary type; manual edits to the monitoring SQL that changed selected columns.","solutions":["Do not modify the built-in LSN query SQL; keep the SELECT expressions unchanged","Verify the tiberius driver version matches what the connector expects and binds `&[u8]` correctly","Log the raw bytes on failure to diagnose the actual column type/length","If a driver upgrade changed binary decoding, adjust the try_get type or convert with an explicit cast in SQL (e.g. `CAST(... AS BINARY(10))`)"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":"fn is_valid_lsn(bytes: &[u8]) -> bool { bytes.len() == 10 }","tryCatchPattern":"match res {\n    Err(e) if e.to_string().contains(\"LSN should be 10 bytes\") => {\n        error!(\"Unexpected LSN column shape from SQL Server: {}\", e);\n    }\n    other => other?,\n}","preventionTips":["Do not hand-edit the LSN monitoring SQL","Keep the tiberius driver version pinned to what the connector supports","Add an SQL-side CAST(... AS BINARY(10)) if the driver decodes differently"],"tags":["cdc","sql-server","lsn","data-format"],"backgroundTag":"invalid-argument-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"}