{"record":{"id":"f177c32ca417ca96","repo":"risingwavelabs/risingwave","slug":"jnierror-error","errorCode":null,"errorMessage":"JniError {error}","messagePattern":"JniError (.+?)","errorType":"exception","errorClass":"BindingError::Jni","httpStatus":null,"severity":"error","filePath":"src/jni_core/src/lib.rs","lineNumber":84,"sourceCode":"use tokio::sync::mpsc::{Receiver, Sender};\nuse tracing_slf4j::*;\n\n/// Enable JVM and Java libraries.\n///\n/// This macro forces this crate to be linked, which registers the JVM builder.\n#[macro_export]\nmacro_rules! enable {\n    () => {\n        use risingwave_jni_core as _;\n    };\n}\n\npub static JAVA_BINDING_ASYNC_RUNTIME: LazyLock<Runtime> =\n    LazyLock::new(|| tokio::runtime::Runtime::new().unwrap());\n\n#[derive(Error, Debug)]\npub enum BindingError {\n    #[error(\"JniError {error}\")]\n    Jni {\n        #[from]\n        error: jni::errors::Error,\n        backtrace: Backtrace,\n    },\n\n    #[error(\"StorageError {error}\")]\n    Storage {\n        #[from]\n        error: anyhow::Error,\n        backtrace: Backtrace,\n    },\n\n    #[error(\"DecodeError {error}\")]\n    Decode {\n        #[from]\n        error: DecodeError,\n        backtrace: Backtrace,","sourceCodeStart":66,"sourceCodeEnd":102,"githubUrl":"https://github.com/risingwavelabs/risingwave/blob/6469eb736d691e8e9b8a419a57edd6429ca77417/src/jni_core/src/lib.rs#L66-L102","documentation":"BindingError::Jni is the variant of the risingwave_jni_core BindingError enum that wraps any jni::errors::Error produced while calling into the JNI API from Rust (e.g. env.get_string, array access, class lookups). The jni crate returns these errors instead of panicking when a JNI call fails, such as when a passed Java object handle is invalid, a type is wrong, or an exception is pending on the thread. The BindingError is converted by execute_and_catch into a thrown Java exception on the calling side.","triggerScenarios":"Any JNI entry point in the binding (string extraction via env.get_string, byte-array access via to_guarded_slice/get_array_elements, JObject casts) receives an invalid, null, or wrong-typed Java object, or a Java exception is already pending when the native code makes another JNI call.","commonSituations":"Java side passes null where a String or byte[] is expected; calling a binding after the JVM changed the type of a field; a previously thrown Java exception was not cleared before re-entering native code; stale local references used across calls.","solutions":["Check the Java call site for null or wrongly-typed arguments before invoking the native binding; the message names the wrapped jni::errors::Error which identifies the exact failing JNI operation","If the error is JavaException, inspect the pending Java exception on the thread — it was thrown by Java code invoked from Rust and must be handled/cleared there","Rebuild/verify JVM and binding versions match: stale JAR/so combinations cause JniError::MethodNotFound/ClassNotFound","Wrap the JNI call site with env.exception_check()/exception_clear() diagnostics to find the pending exception"],"exampleFix":"// before\nlet msg: String = env.get_string(&jstr)?.into(); // panics/throws on null jstr\n// after\nif jstr.is_null() {\n    return Err(BindingError::Jni { error: jni::errors::Error::NullPtr, backtrace: Backtrace::capture() });\n}\nlet msg: String = env.get_string(&jstr)?.into();","handlingStrategy":"try-catch","validationCode":"// Java, before calling any binding method\nif (jstr == null) throw new IllegalArgumentException(\"binding argument must not be null\");","typeGuard":"// Java\nstatic boolean isValidJStringArg(Object arg) {\n    return arg instanceof String && !((String) arg).isEmpty();\n}","tryCatchPattern":"try {\n    Binding.someNativeMethod(arg);\n} catch (RuntimeException e) {\n    if (e.getMessage() != null && e.getMessage().contains(\"JniError\")) {\n        // inspect wrapped jni::errors::Error report, log and rethrow as domain error\n    }\n    throw e;\n}","preventionTips":["Null-check every argument on the Java side before native calls","Keep the JAR and native library versions in lockstep","Clear pending Java exceptions before re-entering native code","Never pass stale local references across JNI calls"],"tags":["jni","rust","java","native-binding"],"backgroundTag":"null-argument","analyzedSha":"6469eb736d691e8e9b8a419a57edd6429ca77417","analyzedAt":"2026-09-11T21:06:21.487Z","contentChangedAt":"2026-09-11T21:06:21.487Z","schemaVersion":2},"datasetVersion":"2026-09-14T11:17:12.474Z"}