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

  1. Read the two logger.info lines — they name the exact underlying failure from both load attempts
  2. 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
  3. Build the native library from source and load it via -Djava.library.path so loadFromSystemPath() succeeds
  4. Point -Djava.io.tmpdir at a writable, exec-mounted directory
  5. 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

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


AI-assisted analysis of tursodatabase/turso@bad083fafb (2026-08-16). Data as JSON: /api/errors/a43acc39366042e6. Report an issue: GitHub.