risingwavelabs/risingwave · error

Unsupported version string in iceberg time travel

Error message

Unsupported version string in iceberg time travel

What it means

to_iceberg_time_travel_as_of maps an AsOf clause to IcebergTimeTravelInfo. Iceberg time travel supports numeric snapshot versions and timestamps, but a string version (AsOf::VersionString) is not supported, so the function bails with this error.

Solutions

  1. Use a numeric snapshot ID: AS OF VERSION 1234567890.
  2. Look up the numeric snapshot ID from the Iceberg metadata and pass it unquoted.
  3. Use AS OF TIMESTAMP with an RFC3339 string if you want time-based travel instead of version-based.

Example fix

// before
SELECT * FROM iceberg_table AS OF VERSION '1234567890';
// after
SELECT * FROM iceberg_table AS OF VERSION 1234567890;
Defensive patterns

Strategy: validation

Validate before calling

// ensure Iceberg snapshot version is numeric before query
let version: i64 = snapshot_id.parse().expect("AS OF VERSION must be numeric for Iceberg");

Type guard

fn is_numeric_version(s: &str) -> bool { s.parse::<i64>().is_ok() }

Try / catch

match res {
    Err(e) if e.to_string().contains("Unsupported version string") => eprintln!("Iceberg time travel requires a numeric AS OF VERSION; use AS OF TIMESTAMP for time-based travel"),
    other => other?,
}

Prevention

When it happens

Trigger: Querying an Iceberg table/metadata table with AS OF VERSION '<string>' where the version is a quoted string rather than a number, e.g. AS OF VERSION 'v3' or AS OF VERSION '123' (string-typed).

Common situations: Users referencing snapshots by name or by quoted snapshot IDs; SQL drivers that pass version literals as strings; documentation examples using string versions.

Understand the failure class

Background: UnsupportedOperationException and "is not supported" errors: when a library deliberately refuses a call — this error's family across 30 libraries.

Related errors


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

Appendix: source

Thrown at src/frontend/src/optimizer/plan_node/utils.rs:585

                .ok_or_else(|| anyhow!("invalid timestamp"))?
        }
    };
    Ok(Some(PbBatchQueryEpoch {
        epoch: Some(batch_query_epoch::PbEpoch::TimeTravel(
            unix_timestamp_sec_to_epoch(timestamp).0,
        )),
    }))
}

pub fn to_iceberg_time_travel_as_of(
    a: &Option<AsOf>,
    timezone: &String,
) -> Result<Option<IcebergTimeTravelInfo>> {
    Ok(match a {
        Some(AsOf::VersionNum(v)) => Some(IcebergTimeTravelInfo::Version(*v)),
        Some(AsOf::TimestampNum(ts)) => Some(IcebergTimeTravelInfo::TimestampMs(ts * 1000)),
        Some(AsOf::VersionString(_)) => {
            bail!("Unsupported version string in iceberg time travel")
        }
        Some(AsOf::TimestampString(ts)) => {
            let date_time = speedate::DateTime::parse_str_rfc3339(ts)
                .map_err(|_e| anyhow!("failed to parse the timestamp"))?;
            let timestamp = if date_time.time.tz_offset.is_none() {
                // If the input does not specify a time zone, use the time zone set by the "SET TIME ZONE" command.
                let tz = Timestamptz::lookup_time_zone(timezone).map_err(|e| anyhow!(e))?;
                match tz.with_ymd_and_hms(
                    date_time.date.year.into(),
                    date_time.date.month.into(),
                    date_time.date.day.into(),
                    date_time.time.hour.into(),
                    date_time.time.minute.into(),
                    date_time.time.second.into(),
                ) {
                    MappedLocalTime::Single(d) => Ok(d.timestamp()),
                    MappedLocalTime::Ambiguous(_, _) | MappedLocalTime::None => {
                        Err(anyhow!(format!(

View on GitHub (pinned to 6469eb736d)