jwtk/jjwt · critical · UnknownClassException

Unable to load class named [${fqcn}] from the thread context

Error message

Unable to load class named [${fqcn}] from the thread context, current, or system/application ClassLoaders.  All heuristics have been exhausted.  Class could not be found.${implHint}

What it means

UnknownClassException thrown by io.jsonwebtoken.lang.Classes.forName when the named class cannot be loaded from the thread context, current, or system/application classloaders after all lookup heuristics fail. jjwt uses reflection to locate its default implementation classes, so this typically means the required jjwt module jar is absent from the runtime classpath.

Source

Thrown at api/src/main/java/io/jsonwebtoken/lang/Classes.java:90

        Class<?> clazz = THREAD_CL_ACCESSOR.loadClass(fqcn);

        if (clazz == null) {
            clazz = CLASS_CL_ACCESSOR.loadClass(fqcn);
        }

        if (clazz == null) {
            clazz = SYSTEM_CL_ACCESSOR.loadClass(fqcn);
        }

        if (clazz == null) {
            String msg = "Unable to load class named [" + fqcn + "] from the thread context, current, or " +
                    "system/application ClassLoaders.  All heuristics have been exhausted.  Class could not be found.";

            if (fqcn != null && fqcn.startsWith("io.jsonwebtoken.impl")) {
                msg += "  Have you remembered to include the jjwt-impl.jar in your runtime classpath?";
            }

            throw new UnknownClassException(msg);
        }

        return (Class<T>) clazz;
    }

    /**
     * Returns the specified resource by checking the current thread's
     * {@link Thread#getContextClassLoader() context class loader}, then the
     * current ClassLoader (<code>Classes.class.getClassLoader()</code>), then the system/application
     * ClassLoader (<code>ClassLoader.getSystemClassLoader()</code>, in that order, using
     * {@link ClassLoader#getResourceAsStream(String) getResourceAsStream(name)}.
     *
     * @param name the name of the resource to acquire from the classloader(s).
     * @return the InputStream of the resource found, or <code>null</code> if the resource cannot be found from any
     * of the three mentioned ClassLoaders.
     * @since 0.8
     */
    public static InputStream getResourceAsStream(String name) {

View on GitHub (pinned to fb71496164)

Solutions

  1. Add jjwt-impl and a serializer module (jjwt-jackson or jjwt-gson) to the runtime classpath, e.g. io.jsonwebtoken:jjwt-impl:<same-version> and io.jsonwebtoken:jjwt-jackson:<same-version>
  2. Verify the jjwt-impl jar version matches the jjwt-api version exactly
  3. Check that the thread context classloader can see the jar (app servers/OSGi); set Thread.currentThread().setContextClassLoader(...) or declare the dependency in the right module
  4. Confirm the fully qualified class name passed to Classes.forName is spelled correctly

Example fix

// before (Maven)
<dependency>
  <groupId>io.jsonwebtoken</groupId>
  <artifactId>jjwt-api</artifactId>
  <version>0.11.5</version>
</dependency>
// after
<dependency>
  <groupId>io.jsonwebtoken</groupId>
  <artifactId>jjwt-api</artifactId>
  <version>0.11.5</version>
</dependency>
<dependency>
  <groupId>io.jsonwebtoken</groupId>
  <artifactId>jjwt-impl</artifactId>
  <version>0.11.5</version>
  <scope>runtime</scope>
</dependency>
<dependency>
  <groupId>io.jsonwebtoken</groupId>
  <artifactId>jjwt-jackson</artifactId>
  <version>0.11.5</version>
  <scope>runtime</scope>
</dependency>
Defensive patterns

Strategy: fallback

Validate before calling

if (!Classes.isAvailable("io.jsonwebtoken.impl.DefaultJwtBuilder")) {
    throw new IllegalStateException("jjwt-impl (and a JSON serializer module) missing from runtime classpath");
}

Try / catch

try {
    return Jwts.parserBuilder().build();
} catch (UnknownClassException e) {
    throw new IllegalStateException("Add jjwt-impl and jjwt-jackson to the runtime classpath", e);
}

Prevention

When it happens

Trigger: Calling any jjwt API that resolves an implementation class by name (Classes.forName, and transitively isAvailable/newInstance) while the class's jar is not on the classpath. The message gains the jjwt-impl.jar hint when the fqcn starts with io.jsonwebtoken.impl.

Common situations: Declaring only jjwt-api as a dependency without jjwt-impl (and jjwt-jackson/gson); runtime classpath assembled manually without the impl jar; shaded/multi-classloader environments (app servers, OSGi, Android) where the thread context classloader differs.

Related errors


AI-assisted analysis of jwtk/jjwt@fb71496164 (2026-09-09). Data as JSON: /api/errors/25268b90be84cf83. Report an issue: GitHub.