risingwavelabs/risingwave · error

SQL Server min_lsn is NULL

Error message

SQL Server min_lsn is NULL

What it means

Same pattern as the max_lsn check: the min_lsn column, computed as `MIN(sys.fn_cdc_get_min_lsn(capture_instance))` over `cdc.change_tables`, is NULL, so hex conversion is impossible and this error is thrown. NULL here typically means no capture instances exist for the change tables.

Source

Thrown at src/connector/src/source/cdc/enumerator/mod.rs:353

                hex_string.push_str(&format!("{:02x}", byte));
            }
            hex_string.push(':');
            for byte in &bytes[8..10] {
                hex_string.push_str(&format!("{:02x}", byte));
            }
            Ok(hex_string)
        };

        let max_lsn = row
            .try_get::<&[u8], usize>(0)?
            .map(lsn_bytes_to_hex)
            .transpose()?
            .ok_or_else(|| anyhow!("SQL Server max_lsn is NULL"))?;
        let min_lsn = row
            .try_get::<&[u8], usize>(1)?
            .map(lsn_bytes_to_hex)
            .transpose()?
            .ok_or_else(|| anyhow!("SQL Server min_lsn is NULL"))?;

        Ok(Some((min_lsn, max_lsn)))
    }

    async fn monitor_sql_server_lsns(&mut self) -> ConnectorResult<()> {
        let lsns = self.query_sql_server_lsns().await.with_context(|| {
            format!(
                "failed to query SQL Server LSNs for source {}",
                self.source_id
            )
        })?;
        if let Some((min_lsn, max_lsn)) = lsns {
            let labels = vec![self.source_id.to_string()];

            if let Some(value) = Self::sql_server_lsn_to_i64(&min_lsn) {
                get_or_create_guarded_int_gauge(
                    &mut self.sqlserver_cdc_upstream_min_lsn,
                    &self.metrics.sqlserver_cdc_upstream_min_lsn,

View on GitHub (pinned to 6469eb736d)

Solutions

  1. Re-enable CDC capture instances on the source table (`sys.sp_cdc_enable_table`)
  2. Verify `SELECT capture_instance FROM cdc.change_tables` returns the expected instances
  3. Restart the CDC capture job so the low water mark is established
  4. If the table was dropped/altered, recreate the RW CDC source against a valid capture instance

Example fix

// re-create capture instance
EXEC sys.sp_cdc_enable_table
  @source_schema = N'dbo', @source_name = N'my_table',
  @role_name = NULL;
Defensive patterns

Strategy: validation

Validate before calling

// Precheck on SQL Server:
// SELECT COUNT(*) FROM cdc.change_tables;
// SELECT MIN(sys.fn_cdc_get_min_lsn(capture_instance)) FROM cdc.change_tables;

Try / catch

match res {
    Err(e) if e.to_string().contains("min_lsn is NULL") => {
        warn!("CDC min LSN unavailable; check capture instances on cdc.change_tables");
    }
    other => other?,
}

Prevention

When it happens

Trigger: The LSN query returns a row whose second column (min_lsn) is NULL — `cdc.change_tables` has rows but `fn_cdc_get_min_lsn` yields NULL, or aggregates to NULL over an empty set.

Common situations: CDC table cleanup jobs removed capture instances; capture instances were dropped while the RW source still monitors them; database restored without restoring CDC metadata; low water mark never initialized.

Related errors


AI-assisted analysis of risingwavelabs/risingwave@6469eb736d (2026-09-11). Data as JSON: /api/errors/eea16422abfe55f3. Report an issue: GitHub.