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

Fail to check exist

Error message

Fail to check exist

What it means

After writing the new metadata file, commit_table must rewrite `version-hint.text`. Before overwriting it calls file_io.exists; if that existence check itself fails (I/O, auth, network), this error wraps the failure because the catalog cannot safely proceed to delete/rewrite the hint.

Source

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

        // # NOTE
        // Iceberg rust didn't support rename operation now, so this commit operation is not atomic.
        let final_metadata_file_path = format!(
            "{table_path}/metadata/v{}.metadata.json",
            current_version + 1
        );
        self.file_io()
            .new_output(final_metadata_file_path)?
            .write(serde_json::to_string(&next_metadata)?.into())
            .await?;

        // write version hint
        let final_file_path = format!("{table_path}/metadata/version-hint.text");
        if self
            .file_io()
            .exists(final_file_path.as_str())
            .await
            .map_err(|_| Error::new(ErrorKind::Unexpected, "Fail to check exist"))?
        {
            self.file_io().delete(final_file_path.as_str()).await?;
        }
        self.file_io()
            .new_output(final_file_path)?
            .write(format!("{}", current_version + 1).into())
            .await?;

        Ok(())
    }
}

#[async_trait]
impl Catalog for StorageCatalog {
    /// List namespaces from table.
    async fn list_namespaces(
        &self,
        _parent: Option<&NamespaceIdent>,

View on GitHub (pinned to 6469eb736d)

Solutions

  1. Check the underlying cause in the chained error source (the wrapped error is discarded here, so enable file_io/opendal logging to see the root cause).
  2. Verify storage credentials and that the principal has read (HEAD) permission on the table's metadata path.
  3. Test connectivity to the object store endpoint (network, DNS, region config).
  4. Retry the commit after transient storage errors are resolved.
Defensive patterns

Strategy: retry

Validate before calling

// preflight storage access before committing
file_io.exists(&format!("{table_path}/metadata/version-hint.text")).await
    .expect("object store unreachable or unauthorized; fix credentials/endpoint before commit");

Try / catch

// bounded retry for transient storage failures
for attempt in 0..3 {
    match catalog.commit_table(path, metadata.clone()).await {
        Err(e) if e.to_string().contains("Fail to check exist") && attempt < 2 => {
            tokio::time::sleep(Duration::from_secs(1 << attempt)).await;
            continue;
        }
        other => break other,
    }
}

Prevention

When it happens

Trigger: Calling update_table where the storage backend returns an error on HEAD/stat of `{table_path}/metadata/version-hint.text`: expired or missing credentials, transient network failure, bucket/permission changes mid-operation.

Common situations: S3/GCS/Azure credentials rotated or revoked during a long job; bucket policy change removing HeadObject permission; region/endpoint misconfiguration; object-store outage.

Understand the failure class

Background: 'Something went wrong' / 'Request failed (500)' / 'HTTP error! status: 404' — what failed HTTP requests actually mean and how to find the real cause — this error's family across 28 libraries.

Related errors


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