risingwavelabs/risingwave · error · ConnectorError
invalid scan.startup.mode, accept earliest/latest/timestamp
Error message
invalid scan.startup.mode, accept earliest/latest/timestamp
What it means
The NATS source reader only accepts 'earliest', 'latest', or 'timestamp' as scan.startup.mode; any other value is rejected at reader construction. This guards against typos and unsupported startup strategies reaching the consumer.
Source
Thrown at src/connector/src/source/nats/source/reader.rs:78
// 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)?;
let (consumer, client) = properties
.common
.build_consumer(
properties.stream.clone(),View on GitHub (pinned to 6469eb736d)
Solutions
- Set scan.startup.mode to exactly one of 'earliest', 'latest', or 'timestamp' (lowercase).
- If migrating from Kafka defaults, use 'earliest' or 'latest' instead of Kafka-specific names.
- For timestamp mode, also supply scan.startup.timestamp.millis.
Example fix
// before WITH (connector = 'nats', scan.startup.mode = 'beginning') // after WITH (connector = 'nats', scan.startup.mode = 'earliest')
Defensive patterns
Strategy: validation
Validate before calling
const VALID: [&str; 3] = ["earliest", "latest", "timestamp"];
if let Some(mode) = props.get("scan.startup.mode") {
if !VALID.contains(&mode.as_str()) && mode != "timestamp_millis" {
return Err(format!("invalid scan.startup.mode: {mode}"));
}
} Type guard
fn is_valid_nats_startup_mode(mode: &str) -> bool {
matches!(mode, "earliest" | "latest" | "timestamp" | "timestamp_millis")
} Try / catch
match source.create().await {
Err(e) if e.to_string().contains("invalid scan.startup.mode") => eprintln!("use earliest/latest/timestamp"),
other => other,
} Prevention
- Keep a shared constant list of valid startup modes per connector.
- Use schema-driven WITH-clause validation in tooling before submitting DDL.
- Never copy Kafka-specific startup mode names into NATS sources.
When it happens
Trigger: Creating a NATS source with `scan.startup.mode` set to a value outside {earliest, latest, timestamp, timestamp_millis} — e.g. 'beginning', 'default', 'EARLIEST', or 'from_timestamp'.
Common situations: Copying Kafka-style startup options ('earliest_offset'/'latest_offset') into a NATS source; case-sensitivity mistakes; typos like 'earlies'.
Understand the failure class
Background: Invalid enum value errors: "Unknown type", "Invalid scope", "must be one of" — when a string is not on the library's allowed list — this error's family across 23 libraries.
Related errors
- NATS connect mode must be one of `user_and_password`, `crede
- NATS connect mode `user_and_password` requires both `user` a
- NATS connect mode `credential` requires both `nkey` and `jwt
- scan.startup.timestamp.millis is required
- `startup_mode` must be `earliest`, `latest`, or empty
AI-assisted analysis of risingwavelabs/risingwave@6469eb736d (2026-09-11).
Data as JSON: /api/errors/c432df2bae9a8653.
Report an issue: GitHub.