risingwavelabs/risingwave · error · anyhow

Caught Java Exception: {}

Error message

Caught Java Exception: {}

What it means

After invoking a Java method through JNI (via the execute_with_jni_env wrapper used by all Iceberg REST catalog operations), the code checks ExceptionCheck. If a Java exception is pending, its message is extracted via getMessage() and returned as a Rust error prefixed with `Caught Java Exception:`. This surfaces underlying Java-side failures (network to catalog, auth, schema issues) into Rust error handling.

Source

Thrown at src/jni_core/src/jvm_runtime.rs:254

        &system_class_loader
    )?;

    let ret = f(&mut env);

    match env.exception_check() {
        Ok(true) => {
            let exception = env.exception_occurred().inspect_err(|e| {
                tracing::warn!(error = %e.as_report(), "Failed to get jvm exception");
            })?;
            env.exception_describe().inspect_err(|e| {
                tracing::warn!(error = %e.as_report(), "Failed to describe jvm exception");
            })?;
            env.exception_clear().inspect_err(|e| {
                tracing::warn!(error = %e.as_report(), "Exception occurred but failed to clear");
            })?;
            let message = call_method!(env, exception, {String getMessage()})?;
            let message = jobj_to_str(&mut env, message)?;
            return Err(anyhow::anyhow!("Caught Java Exception: {}", message));
        }
        Ok(false) => {
            // No exception, do nothing
        }
        Err(e) => {
            tracing::warn!(error = %e.as_report(), "Failed to check exception");
        }
    }

    ret
}

/// A helper method to convert an java object to rust string.
pub fn jobj_to_str(env: &mut JNIEnv<'_>, obj: JObject<'_>) -> anyhow::Result<String> {
    if !env.is_instance_of(&obj, "java/lang/String")? {
        bail!("Input object is not a java string and can't be converted!")
    }
    let jstr = JString::from(obj);

View on GitHub (pinned to 6469eb736d)

Solutions

  1. Read the Java message after `Caught Java Exception:` — it names the actual Java-side cause.
  2. Verify the Iceberg catalog URI, credentials, and warehouse config are correct.
  3. Ensure all required jars are present in CONNECTOR_LIBS_PATH (classpath complete).
  4. Fix the specific catalog error (auth, existing object, network) indicated by the message.
  5. Re-run with Java-side logging enabled for a full stack trace.

Example fix

// before
LOAD TABLE iceberg_t FROM iceberg WITH (catalog.uri='http://wrong-host:9000/catalog')
// after
LOAD TABLE iceberg_t FROM iceberg WITH (catalog.uri='http://catalog-host:9000/catalog', catalog.credential='...')
Defensive patterns

Strategy: try-catch

Validate before calling

// preflight: catalog reachable
const res = await fetch(catalogUri); if (!res.ok) throw new Error(`iceberg catalog ${catalogUri} returned ${res.status}`);

Try / catch

match catalog.list_namespaces() {
    Err(e) if e.to_string().starts_with("Caught Java Exception:") => {
        let java_msg = e.to_string();
        // inspect java_msg for cause: auth, not-found, already-exists, network
    }
    other => other?,
}

Prevention

When it happens

Trigger: Any of list_namespaces, create_namespace, namespace_exists, list_tables, create_table, load_table raising a Java exception on the JNI side — e.g. Iceberg REST catalog returning HTTP 4xx/5xx, missing classpath entries causing ClassNotFoundException, or invalid arguments from Rust.

Common situations: Iceberg REST catalog unreachable or misconfigured (wrong URI/credentials), missing Debezium/iceberg jars on the classpath, OAuth token invalid on the catalog side, table/namespace already exists.

Related errors


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