jni: convert C++ exceptions in recognizer/punctuation constructors to Java exceptions (#3988)

An exception escaping a JNI boundary calls std::terminate and aborts the
process. In production we observed an Ort::Exception raised while loading
a corrupt model file inside OfflineRecognizer::newFromFile take down the
whole app, uncatchable from Kotlin.

Wrap every model-loading JNI constructor (offline/online recognizer,
offline/online punctuation, both the file and the asset variants) in a
try/catch that converts the C++ exception into a Java exception via
ThrowNew, following the existing pattern in jni/wave-reader.cc. The
helper RethrowCxxExceptionAsJava lives in jni/common.h.

Fixes #3987
This commit is contained in:
Paolo Antinori authored and GitHub committed 2026-10-12 09:35:56 +08:00
1 parent 9db1af1871
commit 6e7ea3e409
5 files changed
+88 -13

No files matched your search

+33
View File
@@ -5,8 +5,10 @@
#ifndef SHERPA_ONNX_JNI_COMMON_H_
#define SHERPA_ONNX_JNI_COMMON_H_
#include <exception> // NOLINT
#include <string>
#include "sherpa-onnx/csrc/macros.h"
#include "sherpa-onnx/csrc/text-utils.h"
#if __ANDROID_API__ >= 9
@@ -223,4 +225,35 @@ namespace sherpa_onnx {
void PrependAdspLibraryPath(const std::string &new_path);
}
// Convert a C++ exception caught in a JNI function into a Java exception, so
// callers on the Kotlin/Java side can catch it. Without this, an exception
// escaping a JNI boundary calls std::terminate and aborts the process (e.g. an
// Ort::Exception raised while loading a corrupt model file).
inline void RethrowCxxExceptionAsJava(JNIEnv *env, const std::exception &e,
const char *context) {
SHERPA_ONNX_LOGE("%s: %s", context, e.what());
// JNI string functions expect modified UTF-8; an arbitrary what() buffer is
// not guaranteed to be valid, and ThrowNew with malformed input is
// undefined at the boundary. Fall back to the (ASCII, literal) context when
// the message is not printable ASCII, so the Java side always gets a valid
// string; the full message is preserved in the log line above.
const char *msg = e.what();
bool printable = msg[0] != '\0';
for (const char *p = msg; *p; ++p) {
unsigned char c = static_cast<unsigned char>(*p);
if (c < 0x20 || c > 0x7e) {
printable = false;
break;
}
}
jclass exception_class = env->FindClass("java/lang/Exception");
if (exception_class == nullptr) {
// FindClass itself failed; nothing more we can do here.
return;
}
env->ThrowNew(exception_class, printable ? msg : context);
env->DeleteLocalRef(exception_class);
}
#endif // SHERPA_ONNX_JNI_COMMON_H_
+16 -4
View File
@@ -63,11 +63,17 @@ Java_com_k2fsa_sherpa_onnx_OfflinePunctuation_newFromAsset(
SHERPA_ONNX_LOGE("config:\n%s", config.ToString().c_str());
auto model = new sherpa_onnx::OfflinePunctuation(
sherpa_onnx::OfflinePunctuation *model = nullptr;
try {
model = new sherpa_onnx::OfflinePunctuation(
#if __ANDROID_API__ >= 9
mgr,
mgr,
#endif
config);
config);
} catch (const std::exception &e) {
RethrowCxxExceptionAsJava(env, e, "Failed to create OfflinePunctuation");
return 0;
}
return (jlong)model;
}
@@ -92,7 +98,13 @@ Java_com_k2fsa_sherpa_onnx_OfflinePunctuation_newFromFile(JNIEnv *env,
return 0;
}
auto model = new sherpa_onnx::OfflinePunctuation(config);
sherpa_onnx::OfflinePunctuation *model = nullptr;
try {
model = new sherpa_onnx::OfflinePunctuation(config);
} catch (const std::exception &e) {
RethrowCxxExceptionAsJava(env, e, "Failed to create OfflinePunctuation");
return 0;
}
return (jlong)model;
}
+7 -1
View File
@@ -546,7 +546,13 @@ Java_com_k2fsa_sherpa_onnx_OfflineRecognizer_newFromFile(JNIEnv *env,
return 0;
}
auto model = new sherpa_onnx::OfflineRecognizer(config);
sherpa_onnx::OfflineRecognizer *model = nullptr;
try {
model = new sherpa_onnx::OfflineRecognizer(config);
} catch (const std::exception &e) {
RethrowCxxExceptionAsJava(env, e, "Failed to create OfflineRecognizer");
return 0;
}
return (jlong)model;
}
+16 -4
View File
@@ -68,11 +68,17 @@ Java_com_k2fsa_sherpa_onnx_OnlinePunctuation_newFromAsset(JNIEnv *env,
SHERPA_ONNX_LOGE("config:\n%s", config.ToString().c_str());
auto model = new sherpa_onnx::OnlinePunctuation(
sherpa_onnx::OnlinePunctuation *model = nullptr;
try {
model = new sherpa_onnx::OnlinePunctuation(
#if __ANDROID_API__ >= 9
mgr,
mgr,
#endif
config);
config);
} catch (const std::exception &e) {
RethrowCxxExceptionAsJava(env, e, "Failed to create OnlinePunctuation");
return 0;
}
return (jlong)model;
}
@@ -97,7 +103,13 @@ Java_com_k2fsa_sherpa_onnx_OnlinePunctuation_newFromFile(JNIEnv *env,
return 0;
}
auto model = new sherpa_onnx::OnlinePunctuation(config);
sherpa_onnx::OnlinePunctuation *model = nullptr;
try {
model = new sherpa_onnx::OnlinePunctuation(config);
} catch (const std::exception &e) {
RethrowCxxExceptionAsJava(env, e, "Failed to create OnlinePunctuation");
return 0;
}
return (jlong)model;
}
+16 -4
View File
@@ -273,11 +273,17 @@ Java_com_k2fsa_sherpa_onnx_OnlineRecognizer_newFromAsset(JNIEnv *env,
#endif
}
auto recognizer = new sherpa_onnx::OnlineRecognizer(
sherpa_onnx::OnlineRecognizer *recognizer = nullptr;
try {
recognizer = new sherpa_onnx::OnlineRecognizer(
#if __ANDROID_API__ >= 9
mgr,
mgr,
#endif
config);
config);
} catch (const std::exception &e) {
RethrowCxxExceptionAsJava(env, e, "Failed to create OnlineRecognizer");
return 0;
}
return (jlong)recognizer;
}
@@ -309,7 +315,13 @@ JNIEXPORT jlong JNICALL Java_com_k2fsa_sherpa_onnx_OnlineRecognizer_newFromFile(
return 0;
}
auto recognizer = new sherpa_onnx::OnlineRecognizer(config);
sherpa_onnx::OnlineRecognizer *recognizer = nullptr;
try {
recognizer = new sherpa_onnx::OnlineRecognizer(config);
} catch (const std::exception &e) {
RethrowCxxExceptionAsJava(env, e, "Failed to create OnlineRecognizer");
return 0;
}
return (jlong)recognizer;
}