risingwavelabs/risingwave · error

invalid Iceberg snapshot timestamp

Error message

invalid Iceberg snapshot timestamp

What it means

When scanning the iceberg_snapshots metadata table, each snapshot's timestamp_ms is converted to a Timestamptz; a None result (invalid milliseconds) aborts the scan with this error rather than emitting a row with a NULL committed_at.

Solutions

  1. Inspect the table's metadata JSON (v*.metadata.json) for snapshots with invalid 'timestamp-ms'
  2. Repair or regenerate the metadata from a valid snapshot/backup
  3. Query other metadata tables or snapshot history via Iceberg tools to locate the bad snapshot
Defensive patterns

Strategy: try-catch

Validate before calling

let ts_ok = snapshots.iter().all(|s| Timestamptz::from_millis(s.timestamp_ms()).is_some());

Try / catch

match result {
    Err(e) if e.to_string().contains("invalid Iceberg snapshot timestamp") => {
        // skip or repair the offending snapshot metadata
    }
    other => other?,
}

Prevention

When it happens

Trigger: Querying the snapshot metadata table of an Iceberg table whose metadata contains a snapshot with a timestamp_ms value that cannot produce a valid Timestamptz (out of range millis).

Common situations: Corrupted or hand-edited Iceberg metadata JSON, or snapshots written by a buggy external writer with out-of-range timestamps.

Related errors


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

Appendix: source

Thrown at src/connector/src/sink/iceberg/metadata.rs:155

) -> ConnectorResult<Option<DataChunk>> {
    Ok(builder.append_one_row(row.into_owned_row()))
}

/// Read one Iceberg metadata relation and emit bounded [`DataChunk`]s.
#[try_stream(ok = DataChunk, error = ConnectorError)]
pub async fn scan_iceberg_metadata(
    table: Table,
    metadata_type: IcebergMetadataTableType,
    time_travel_info: Option<IcebergTimeTravelInfo>,
    chunk_size: usize,
) {
    let mut builder = DataChunkBuilder::new(metadata_type.schema().data_types(), chunk_size);

    match metadata_type {
        IcebergMetadataTableType::Snapshots => {
            for snapshot in table.metadata().snapshots() {
                let committed_at = Timestamptz::from_millis(snapshot.timestamp_ms())
                    .ok_or_else(|| anyhow!("invalid Iceberg snapshot timestamp"))?;
                let summary =
                    serde_json::to_value(&snapshot.summary().additional_properties)?.into();
                let row = IcebergSnapshotRow {
                    committed_at,
                    snapshot_id: snapshot.snapshot_id(),
                    parent_id: snapshot.parent_snapshot_id(),
                    sequence_number: snapshot.sequence_number(),
                    operation: snapshot.summary().operation.as_str().to_owned(),
                    manifest_list: snapshot.manifest_list().to_owned(),
                    summary,
                };
                if let Some(chunk) = append_row(&mut builder, row)? {
                    yield chunk;
                }
            }
        }
        IcebergMetadataTableType::Manifests | IcebergMetadataTableType::Files => {
            let Some(snapshot_id) =

View on GitHub (pinned to 6469eb736d)