risingwavelabs/risingwave · error · iceberg::Error(ErrorKind::DataInvalid)

parse version hint failed

Error message

parse version hint failed

What it means

The storage (filesystem-based) Iceberg catalog reads the table's `version-hint.text` file to learn the current metadata file version, then tries to parse it as an integer. This error is thrown when the file exists but its contents cannot be parsed as a number, so the catalog cannot determine which `v<N>.metadata.json` file to load or commit.

Source

Thrown at src/connector/src/connector_common/iceberg/storage_catalog.rs:173

    async fn read_version_hint(&self, table_path: &str) -> Result<i32> {
        let content = self
            .file_io
            .new_input(format!("{table_path}/metadata/version-hint.text").as_str())?
            .read()
            .await?;
        let version_hint = String::from_utf8(content.to_vec()).map_err(|err| {
            Error::new(
                ErrorKind::DataInvalid,
                format!(
                    "Fail to convert version_hint from utf8 to string: {}",
                    err.as_report()
                ),
            )
        })?;

        version_hint
            .parse()
            .map_err(|_| Error::new(ErrorKind::DataInvalid, "parse version hint failed"))
    }

    pub fn file_io(&self) -> &FileIO {
        &self.file_io
    }

    fn table_path(&self, table: &TableIdent) -> String {
        let mut names = table.namespace.clone().inner();
        names.push(table.name.clone());
        if self.warehouse.ends_with('/') {
            format!("{}{}", self.warehouse, names.join("/"))
        } else {
            format!("{}/{}", self.warehouse, names.join("/"))
        }
    }

    async fn commit_table(&self, table_path: &str, next_metadata: TableMetadata) -> Result<()> {
        let current_version = if self.is_version_hint_exist(table_path).await? {

View on GitHub (pinned to 6469eb736d)

Solutions

  1. Inspect the contents of `{table_path}/metadata/version-hint.text` and rewrite it as a plain integer equal to the latest metadata version (e.g. `5` for `v5.metadata.json`).
  2. Check the listed `v*.metadata.json` files and set the hint to the highest version number found.
  3. Remove concurrent writers or ensure atomic writes of the version-hint file to prevent truncated content.
  4. If the file is irrecoverable and no writers depend on it, recreate the catalog/table registration against a catalog implementation that does not rely on version hints.

Example fix

// before: file contains "05\n \u{feef}" or garbage
// after: rewrite the hint file with the exact integer
echo -n "3" > s3://bucket/db/table/metadata/version-hint.text
Defensive patterns

Strategy: validation

Validate before calling

let hint = file_io.read(format!("{table_path}/metadata/version-hint.text")).await?;
let s = String::from_utf8_lossy(&hint).trim().to_string();
if s.is_empty() || s.parse::<i64>().is_err() {
    return Err("version-hint.text is not a valid integer; repair before committing");
}

Type guard

fn is_valid_version_hint(raw: &str) -> bool {
    raw.trim().parse::<i64>().is_ok()
}

Prevention

When it happens

Trigger: Calling commit_table or load_table (via update_table/drop_table) when `{table_path}/metadata/version-hint.text` exists but contains non-numeric content: empty file, whitespace/newline-only, BOM characters, or text accidentally written instead of a version integer.

Common situations: A version-hint file truncated by a failed concurrent write, manually edited by an operator, corrupted by a sync tool, or written by a different tool in an incompatible format (e.g. with trailing garbage or a UTF-8 BOM).

Understand the failure class

Background: "Invalid ... format", "must be in format X", "does not look like a ..." — invalid argument format errors across CLI tools and libraries — this error's family across 17 libraries.

Related errors


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