apache/beam · error · RuntimeException

Cannot detect classpath: classload is null (is it the…

Error message

Cannot detect classpath: classload is null (is it the bootstrap classloader?)

What it means

When lazily creating the shared Kryo instance, KryoState reads the current thread's context ClassLoader to scope Kryo's classpath/class resolution. If the context ClassLoader is null (typically only the bootstrap ClassLoader situation), it throws a RuntimeException because Kryo could not resolve application classes. This is an environment/threading problem, not a data problem.

Solutions

  1. Set the context ClassLoader on the thread before decoding: Thread.currentThread().setContextClassLoader(AppClass.class.getClassLoader()).
  2. Configure the runner/worker environment so application threads have a proper context classloader.
  3. If spawning your own threads, pass and set the classloader from the creating thread.
  4. Avoid relying on the bootstrap classloader; ensure code runs on a worker classloader containing user code.

Example fix

// before: custom thread without classloader
new Thread(() -> pipeline.run()).start();
// after
Thread t = new Thread(() -> pipeline.run());
t.setContextClassLoader(MyPipeline.class.getClassLoader());
t.start();
Defensive patterns

Strategy: validation

Validate before calling

if (Thread.currentThread().getContextClassLoader() == null) { Thread.currentThread().setContextClassLoader(MyClass.class.getClassLoader()); }

Prevention

When it happens

Trigger: KryoState.getOrCreate executing on a thread whose context ClassLoader was never set or was explicitly cleared (null), e.g. certain runner/system threads or threads created before classloader configuration.

Common situations: Running on a runner or container whose worker threads lack a context classloader; using Kryo coder inside custom thread pools where Thread.currentThread().setContextClassLoader was never called; bootstrap-classloader-only environments.

Understand the failure class

Background: "environment variable is not set" and "Missing keys in environment" errors: what missing required env var messages mean and how to fix them — this error's family across 28 libraries.

Related errors


AI-assisted analysis of apache/beam@12126d8942 (2026-09-13). Data as JSON: /api/errors/96bb613b9325d596. Report an issue: GitHub.

Appendix: source

Thrown at sdks/java/extensions/kryo/src/main/java/org/apache/beam/sdk/extensions/kryo/KryoState.java:66

    private final ThreadLocal<Map<String, KryoState>> kryoStateMap =
        ThreadLocal.withInitial(HashMap::new);

    KryoState getOrCreate(KryoCoder<?> coder) {
      return kryoStateMap
          .get()
          .computeIfAbsent(
              coder.getInstanceId(),
              k -> {
                final Kryo kryo = new Kryo();
                // fallback in case serialized class does not have default constructor
                kryo.setInstantiatorStrategy(
                    new DefaultInstantiatorStrategy(new StdInstantiatorStrategy()));
                kryo.setReferences(coder.getOptions().getReferences());
                kryo.setRegistrationRequired(coder.getOptions().getRegistrationRequired());

                ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
                if (classLoader == null) {
                  throw new RuntimeException(
                      "Cannot detect classpath: classload is null (is it the bootstrap classloader?)");
                }

                kryo.setClassLoader(classLoader);
                // first id of user provided class registration
                final int firstRegistrationId = kryo.getNextRegistrationId();
                // register user provided classes
                for (KryoRegistrar registrar : coder.getRegistrars()) {
                  registrar.registerClasses(kryo);
                }
                return new KryoState(
                    kryo,
                    firstRegistrationId,
                    new InputChunked(coder.getOptions().getBufferSize()),
                    new OutputChunked(coder.getOptions().getBufferSize()));
              });
    }
  }

View on GitHub (pinned to 12126d8942)