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
- 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`).
- Check the listed `v*.metadata.json` files and set the hint to the highest version number found.
- Remove concurrent writers or ensure atomic writes of the version-hint file to prevent truncated content.
- 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
- Never hand-edit version-hint.text; always write a plain integer with atomic rename
- Exclude the Iceberg metadata/ prefix from object-store lifecycle and sync tools
- Validate hint parseability as part of table health checks
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
- no version hint found for table
- `catalog.type` must be set
- Failed to list iceberg namespaces.
- Failed to load iceberg table.
- Failed to drop iceberg table.
AI-assisted analysis of risingwavelabs/risingwave@6469eb736d (2026-09-11).
Data as JSON: /api/errors/65197c3fb8ef6718.
Report an issue: GitHub.