{"record":{"id":"fade0ffd791a38c9","repo":"risingwavelabs/risingwave","slug":"scan-startup-timestamp-millis-is-required","errorCode":null,"errorMessage":"scan.startup.timestamp.millis is required","messagePattern":"scan\\.startup\\.timestamp\\.millis is required","errorType":"validation","errorClass":"ConnectorError","httpStatus":null,"severity":"error","filePath":"src/connector/src/source/kinesis/source/reader.rs","lineNumber":91,"sourceCode":"        parser_config: ParserConfig,\n        source_ctx: SourceContextRef,\n        _columns: Option<Vec<Column>>,\n    ) -> Result<Self> {\n        assert!(splits.len() == 1);\n\n        let split = splits.into_iter().next().unwrap();\n\n        let next_offset = match &split.next_offset {\n            KinesisOffset::None => match &properties.scan_startup_mode {\n                None => KinesisOffset::Earliest,\n                Some(mode) => match mode.as_str() {\n                    \"earliest\" => KinesisOffset::Earliest,\n                    \"latest\" => KinesisOffset::Latest,\n                    \"timestamp\" => {\n                        if let Some(ts) = &properties.start_timestamp_millis {\n                            KinesisOffset::Timestamp(*ts)\n                        } else {\n                            bail!(\"scan.startup.timestamp.millis is required\");\n                        }\n                    }\n                    _ => {\n                        bail!(\"invalid scan.startup.mode, accept earliest/latest/timestamp\")\n                    }\n                },\n            },\n            next_offset => next_offset.to_owned(),\n        };\n\n        if !matches!(next_offset, KinesisOffset::Timestamp(_))\n            && properties.start_timestamp_millis.is_some()\n        {\n            // cannot bail! here because all new split readers will fail to start if user set 'scan.startup.mode' to 'timestamp'\n            tracing::warn!(\n                \"scan.startup.mode needs to be set to 'timestamp' if you want to start with a specific timestamp, starting shard {} from the beginning\",\n                split.id()\n            );","sourceCodeStart":73,"sourceCodeEnd":109,"githubUrl":"https://github.com/risingwavelabs/risingwave/blob/6469eb736d691e8e9b8a419a57edd6429ca77417/src/connector/src/source/kinesis/source/reader.rs#L73-L109","documentation":"The Kinesis source connector's `new` parses `scan.startup.mode`; when it is set to `\"timestamp\"`, the property `scan.startup.timestamp.millis` (mapped to `properties.start_timestamp_millis`) must also be provided. If it is absent, `bail!` aborts source construction with this message. It enforces that a timestamp startup mode always carries an actual epoch-milliseconds value to seek from.","triggerScenarios":"Creating a Kinesis source with WITH option `scan.startup.mode = 'timestamp'` while omitting `scan.startup.timestamp.millis`.","commonSituations":"Users copy a config template that enables timestamp mode but forget the companion timestamp field; SQL changed via ALTER SOURCE adds the mode without adding the property; config validation passes because the mode value is valid but the pairing is only checked at reader construction.","solutions":["Add `scan.startup.timestamp.millis = '<epoch_ms>'` to the source WITH clause.","If you actually want to start from the oldest data, set `scan.startup.mode = 'earliest'` instead.","Validate the pair of options in the CREATE SOURCE SQL before running it."],"exampleFix":"// before\nWITH (\n  connector = 'kinesis',\n  stream_name = 'events',\n  scan.startup.mode = 'timestamp'\n)\n// after\nWITH (\n  connector = 'kinesis',\n  stream_name = 'events',\n  scan.startup.mode = 'timestamp',\n  scan.startup.timestamp.millis = '1700000000000'\n)","handlingStrategy":"validation","validationCode":"// Before creating/updating the source, validate the option pair\nlet mode = props.get(\"scan.startup.mode\").map(|s| s.as_str());\nif mode == Some(\"timestamp\") {\n    props.get(\"scan.startup.timestamp.millis\")\n        .and_then(|v| v.parse::<i64>().ok())\n        .ok_or_else(|| anyhow!(\"scan.startup.mode='timestamp' requires scan.startup.timestamp.millis (epoch ms)\"))?;\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Always set scan.startup.timestamp.millis together with scan.startup.mode='timestamp' in DDL templates.","Add a pre-deployment config lint that checks required companion fields for each startup mode.","Prefer 'earliest'/'latest' when a precise offset is not needed."],"tags":["kinesis","missing-config-value","startup-mode"],"backgroundTag":"missing-required-config-field","analyzedSha":"6469eb736d691e8e9b8a419a57edd6429ca77417","analyzedAt":"2026-09-11T21:06:21.487Z","contentChangedAt":"2026-09-11T21:06:21.487Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}