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

Failed to create iceberg table.

Error message

Failed to create iceberg table.

What it means

`JniCatalog::create_table` wraps any failure of the JNI create-table call (Java exception, invalid schema/JSON, catalog rejection, or response parse failure) into an iceberg `Unexpected` error with the message 'Failed to create iceberg table.' and the original error as its source.

Source

Thrown at src/connector/src/connector_common/iceberg/jni_catalog.rs:337

                let table_metadata = resp.metadata;

                let file_io =
                    FileIOBuilder::new(Arc::new(OpenDalResolvingStorageFactory::new()))
                    .with_props(file_io_props.iter())
                    .build();

                Ok(Table::builder()
                    .file_io(file_io)
                    .identifier(TableIdent::new(namespace, creation.name))
                    .metadata(table_metadata)
                    .runtime(runtime)
                    .build())
            })
        })
        .await
        .map_err(|e| {
            iceberg::Error::new(
                iceberg::ErrorKind::Unexpected,
                "Failed to create iceberg table.",
            )
            .with_source(e)
        })?
    }

    /// Load table from the catalog.
    async fn load_table(&self, table: &TableIdent) -> iceberg::Result<Table> {
        let inner = self.inner.clone();
        let file_io_props = self.file_io_props.clone();
        let runtime = Runtime::try_current()?;
        let table = table.clone();
        execute_blocking_jni(move || {
            execute_with_jni_env(inner.jvm, |env| {
                let table_name_str = table.to_string();

                let table_name_jstr = env.new_string(&table_name_str).unwrap();

View on GitHub (pinned to 6469eb736d)

Solutions

  1. Inspect the wrapped source error to find the Java-side exception.
  2. Verify the table does not already exist in the catalog.
  3. Check that the namespace exists and credentials/IAM allow table creation at the warehouse location.
  4. Confirm the table schema (types, identifiers) is supported by the backend catalog.
  5. Ensure the JNI/JVM environment and classpath are correctly configured.
Defensive patterns

Strategy: try-catch

Validate before calling

// Validate schema and preconditions before JNI create_table
fn can_create(catalog_exists: bool, table_exists: bool, schema_json: &str) -> Result<(), String> {
    if !catalog_exists { return Err("namespace does not exist".into()); }
    if table_exists { return Err("table already exists".into()); }
    if serde_json::from_str::<serde_json::Value>(schema_json).is_err() { return Err("schema is not valid JSON".into()); }
    Ok(())
}

Try / catch

match catalog.create_table(&ns, ident, schema).await {
    Err(e) => {
        tracing::error!(source = ?e.source(), "iceberg create_table failed");
        // inspect e.source() for Java exception: already-exists vs permissions vs schema
        Err(e)
    }
    Ok(t) => Ok(t),
}

Prevention

When it happens

Trigger: Calling create_table on a JNI catalog when the JVM-side create fails: invalid schema JSON sent to Java, namespace does not exist, table already exists, permission denied, or the returned result JSON cannot be parsed.

Common situations: CREATE TABLE / CREATE SINK into Iceberg with a schema the Java catalog rejects; table already existing in Glue/Hadoop; missing IAM permissions on the target location; malformed or unsupported column types crossing the JNI boundary.

Related errors


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