tursodatabase/turso · critical · InternalError
Unable to load necessary native library
Error message
Unable to load necessary native library
What it means
The SingletonHolder static initializer tries System.loadLibrary("_turso_java") first, then extracts and loads the JAR-bundled library. If both fail it throws InternalError. Because detect() returning UNSUPPORTED, a missing libs/ resource, an unwritable temp dir (convertInputStreamToFile), or System.load failures all land in the same catch, the logger lines 'Unable to load from default path:' and 'Unable to load from jar:' carry the real cause.
Source
Thrown at bindings/java/src/main/java/tech/turso/core/TursoDB.java:110
/**
* This method attempts to load the native library required for turso operations. It first tries
* to load the library from the system's library path using {@link #loadFromSystemPath()}. If that
* fails, it attempts to load the library from the JAR file using {@link #loadFromJar()}. If
* either method succeeds, the `isLoaded` flag is set to true. If both methods fail, an {@link
* InternalError} is thrown indicating that the necessary native library could not be loaded.
*
* @throws InternalError if the native library cannot be loaded from either the system path or the
* JAR file.
*/
private static void load() {
new SingletonHolder();
}
// "lazy initialization holder class idiom" (Effective Java #83)
private static class SingletonHolder {
static {
if (!loadFromSystemPath() && !loadFromJar()) {
throw new InternalError("Unable to load necessary native library");
}
}
}
/**
* Load the native library from the system path.
*
* <p>This method attempts to load the native library named "_turso_java" from the system's
* library path. If the library is successfully loaded, the `isLoaded` flag is set to true.
*
* @return true if the library was successfully loaded, false otherwise.
*/
private static boolean loadFromSystemPath() {
try {
System.loadLibrary("_turso_java");
return true;
} catch (Throwable t) {
logger.info("Unable to load from default path: {}", String.valueOf(t));View on GitHub (pinned to bad083fafb)
Solutions
- Read the two logger.info lines — they name the exact underlying failure from both load attempts
- Verify the JAR actually contains libs/<os>_<arch>/lib_turso_java.* ; if shading stripped it, depend on the unshaded artifact or add the native resource to the shade filter
- Build the native library from source and load it via -Djava.library.path so loadFromSystemPath() succeeds
- Point -Djava.io.tmpdir at a writable, exec-mounted directory
- On Alpine, switch to a glibc-based image or build the library natively for musl
Example fix
# before: shaded jar lost libs/ resources, temp dir noexec java -jar app.jar # InternalError: Unable to load necessary native library # after: writable+exec tmp dir, explicit system-path fallback java -Djava.io.tmpdir=/var/tmp/jni -Djava.library.path=/opt/turso-native -jar app.jar
Defensive patterns
Strategy: fallback
Validate before calling
// fail fast before first use, with the real cause surfaced
String libPath = "/libs/" + (isLinux() ? "linux_x86/lib_turso_java.so" : "");
boolean bundled = TursoDB.class.getResourceAsStream(libPath) != null;
boolean onSystemPath = System.getProperty("java.library.path") != null;
if (!bundled && !onSystemPath) {
throw new IllegalStateException("turso native library missing: no bundled " + libPath);
} Try / catch
try {
Connection c = DriverManager.getConnection(url);
} catch (Throwable t) {
if (t instanceof InternalError || t instanceof ExceptionInInitializerError) {
// log the two 'Unable to load from ...' logger lines; they carry the root cause.
// fall back: provision lib_turso_java via -Djava.library.path and restart
}
} Prevention
- Smoke-test one connection at application start so load failures surface at boot, not mid-request
- When shading, keep libs/** resources in the fat jar (configure the shade filter)
- Ensure java.io.tmpdir is writable and exec-mounted; prefer glibc-based base images
When it happens
Trigger: First TursoDB.create(...) on: an OS/arch where detect() returns UNSUPPORTED (riscv64, ppc64le, ...); a JAR whose libs/ native resources were stripped by shading/repackaging; a read-only or noexec java.io.tmpdir; a bundled .so whose system dependencies (glibc) are missing, e.g. on Alpine/musl.
Common situations: Maven shade/Gradle shadow jars silently dropping libs/** resources; hardened Docker images with noexec TMPDIR; Alpine Linux lacking glibc; SNAPSHOT artifacts published without native classifiers; corrupt artifact downloads.
Related errors
- ARM64 architecture is not supported on Linux yet
- step() returned null, which is only returned when an error o
- SQLite only supports TYPE_FORWARD_ONLY cursors
- SQLite only supports CONCUR_READ_ONLY cursors
- SQLite only supports closing cursors at commit
AI-assisted analysis of tursodatabase/turso@bad083fafb (2026-08-16).
Data as JSON: /api/errors/a43acc39366042e6.
Report an issue: GitHub.