risingwavelabs/risingwave · error

specify at most one of start_offset or start_snapshot

Error message

specify at most one of start_offset or start_snapshot

What it means

PubSub supports seeking either to a timestamp (start_offset) or to a named snapshot (start_snapshot), but not both at once. The enumerator's SeekTo resolution bails if both properties are provided simultaneously. Exactly one of the two must be set (or neither).

Source

Thrown at src/connector/src/source/google_pubsub/enumerator/client.rs:73

            .exists(None)
            .await
            .context("error checking subscription validity")?
        {
            bail!("subscription {} does not exist", &sub.id())
        }

        let seek_to = match (properties.start_offset, properties.start_snapshot) {
            (None, None) => None,
            (Some(start_offset), None) => {
                let ts = start_offset
                    .parse::<i64>()
                    .context("error parsing start_offset")
                    .map(|nanos| Utc.timestamp_nanos(nanos).into())?;
                Some(SeekTo::Timestamp(ts))
            }
            (None, Some(snapshot)) => Some(SeekTo::Snapshot(snapshot)),
            (Some(_), Some(_)) => {
                bail!("specify at most one of start_offset or start_snapshot")
            }
        };

        if let Some(seek_to) = seek_to {
            sub.seek(seek_to, None)
                .await
                .context("error seeking subscription")?;
        }

        Ok(Self {
            subscription: properties.subscription,
        })
    }

    async fn list_splits(&mut self) -> ConnectorResult<Vec<PubsubSplit>> {
        tracing::debug!("enumerating pubsub splits (adaptive mode, returning 1 template split)");
        Ok(vec![PubsubSplit {
            index: 0,

View on GitHub (pinned to 6469eb736d)

Solutions

  1. Remove `start_snapshot` if you want time-based recovery via `start_offset`.
  2. Remove `start_offset` if you want snapshot-based recovery via `start_snapshot`.
  3. Omit both if you want to resume from the subscription's current state (default).

Example fix

-- before
WITH (connector = 'google_pubsub', start_offset = '2024-01-01T00:00:00Z', start_snapshot = 'my-snap')
-- after (keep only one)
WITH (connector = 'google_pubsub', start_offset = '2024-01-01T00:00:00Z')
Defensive patterns

Strategy: validation

Validate before calling

// reject configs specifying both
function pubsubSeekPropsValid(p) {
  return !(p.start_offset && p.start_snapshot);
}

Type guard

function hasNoConflictingSeek(p) {
  return !("start_offset" in p && "start_snapshot" in p);
}

Try / catch

if (props.start_offset && props.start_snapshot) {
  throw new Error("specify at most one of start_offset or start_snapshot");
}

Prevention

When it happens

Trigger: Creating a PubSub source with both `start_offset` and `start_snapshot` specified in the WITH clause, hitting the (Some(_), Some(_)) match arm in PubsubEnumeratorClient::new.

Common situations: Copy-pasting a config template that already had start_snapshot and adding start_offset on top; trying to 'be safe' by specifying both; leftover flags from an incremental config edit.

Related errors


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