risingwavelabs/risingwave · error · ConnectorError

scan.startup.timestamp.millis is required

Error message

scan.startup.timestamp.millis is required

What it means

The NATS source reader requires a startup timestamp when scan.startup.mode is set to 'timestamp' (or the legacy 'timestamp_millis'), but the property scan.startup.timestamp.millis was not provided. Without it there is no point in time from which to begin consuming, so the connector rejects the configuration.

Source

Thrown at src/connector/src/source/nats/source/reader.rs:74

        parser_config: ParserConfig,
        source_ctx: SourceContextRef,
        _columns: Option<Vec<Column>>,
    ) -> Result<Self> {
        // We guarantee the split num always align with parallelism
        assert_eq!(splits.len(), 1);
        let split = splits.into_iter().next().unwrap();
        let split_id = split.split_id;
        let start_position = match &split.start_sequence {
            NatsOffset::None => match &properties.scan_startup_mode {
                None => NatsOffset::Earliest,
                Some(mode) => match mode.as_str() {
                    "latest" => NatsOffset::Latest,
                    "earliest" => NatsOffset::Earliest,
                    "timestamp" | "timestamp_millis" /* backward-compat */ => {
                        if let Some(ts) = &properties.start_timestamp_millis {
                            NatsOffset::Timestamp(*ts)
                        } else {
                            bail!("scan.startup.timestamp.millis is required");
                        }
                    }
                    _ => {
                        bail!("invalid scan.startup.mode, accept earliest/latest/timestamp")
                    }
                },
            },
            // We have record on this Nats Split, contains the last seen offset (seq id) or reply subject
            // We do not use the seq id as start position anymore,
            // but just let the reader load from durable consumer on broker.
            start_position => start_position.to_owned(),
        };

        let mut config = consumer::pull::Config {
            ..Default::default()
        };
        properties.set_config(&mut config)?;

View on GitHub (pinned to 6469eb736d)

Solutions

  1. Add `scan.startup.timestamp.millis = '<epoch_millis>'` to the source WITH clause.
  2. If you want to consume from the beginning instead, set `scan.startup.mode = 'earliest'` and drop the timestamp mode.
  3. Use `scan.startup.mode = 'latest'` to consume only new messages if no historical point is needed.

Example fix

// before
WITH (connector = 'nats', scan.startup.mode = 'timestamp')
// after
WITH (connector = 'nats', scan.startup.mode = 'timestamp', scan.startup.timestamp.millis = '1700000000000')
Defensive patterns

Strategy: validation

Validate before calling

if mode == "timestamp" && !props.contains_key("scan.startup.timestamp.millis") {
    return Err("scan.startup.mode='timestamp' requires scan.startup.timestamp.millis");
}

Type guard

fn require_timestamp_millis(mode: &str, millis: Option<&str>) -> Option<u64> {
    match mode {
        "timestamp" | "timestamp_millis" => millis?.parse::<u64>().ok(),
        _ => None,
    }
}

Try / catch

match source.create().await {
    Err(e) if e.to_string().contains("scan.startup.timestamp.millis is required") => fix_and_recreate_source(),
    other => other,
}

Prevention

When it happens

Trigger: Creating a NATS source with `scan.startup.mode = 'timestamp'` (or 'timestamp_millis') while omitting `scan.startup.timestamp.millis` in the WITH clause.

Common situations: Copy-pasting a source DDL with timestamp startup mode from an example that had the millis field; renaming the property but forgetting to add it; assuming the current time is used as a default.

Understand the failure class

Background: "is required", "must be set", "missing required field": configuration validation errors across open-source libraries — this error's family across 36 libraries.

Related errors


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