Skip to content

[Bug] ServiceConfigBase.checkRef throws NullPointerException instead of reporting the unimplemented interface #16493

Open

Description

@obabichevjb

Pre-check

  • I am sure that all the content I provide is in English.

Search before asking

  • I had searched in the issues and found no similar issues.

Apache Dubbo Component

Java SDK (apache/dubbo)

Dubbo Version

Dubbo Java 3.3.6, OpenJDK 21 (Amazon Corretto 21.0.5), macOS 15.6 (OS-independent)

Steps to reproduce this issue

ServiceConfigBase.checkRef reports a service whose ref does not implement the declared
interface. Its message is built by getClassDesc, which dereferences Class#getClassLoader():

private String getClassDesc(Class clazz) {
    ClassLoader classLoader = clazz.getClassLoader();
    return clazz.getName() + "[classloader=" + classLoader.getClass().getName() + "@"
            + classLoader.hashCode() + "]";
}

Class#getClassLoader() returns null for any bootstrap-loaded class, i.e. every JDK type. So
when a JDK type is on either side of the mismatch, the check throws NullPointerException from
inside its own diagnostic and the real misconfiguration is never reported.

Export a service whose declared interface is a JDK type that the ref does not implement. This is a
complete main, with a dubbo dependency and nothing else — no registry or other infrastructure:

import org.apache.dubbo.config.ApplicationConfig;
import org.apache.dubbo.config.ProtocolConfig;
import org.apache.dubbo.config.RegistryConfig;
import org.apache.dubbo.config.ServiceConfig;
import org.apache.dubbo.config.bootstrap.DubboBootstrap;

public class JavaRepro {
    public static void main(String[] args) {
        ServiceConfig<Object> service = new ServiceConfig<>();
        service.setInterface(Runnable.class); // GreeterServiceImpl does not implement Runnable
        service.setRef(new GreeterServiceImpl());

        DubboBootstrap.newInstance()
                .application(new ApplicationConfig("java-repro"))
                .registry(new RegistryConfig("N/A"))
                .protocol(new ProtocolConfig("dubbo", 20896))
                .service(service)
                .start();
    }
}

Result on 3.3.6:

Exception in thread "main" java.lang.NullPointerException: Cannot invoke "Object.getClass()" because "classLoader" is null
    at org.apache.dubbo.config.ServiceConfigBase.getClassDesc(ServiceConfigBase.java:162)
    at org.apache.dubbo.config.ServiceConfigBase.checkRef(ServiceConfigBase.java:156)
    at org.apache.dubbo.config.ServiceConfig.checkAndUpdateSubConfigs(ServiceConfig.java:508)
    at org.apache.dubbo.config.ServiceConfig.postProcessRefresh(ServiceConfig.java:549)
    at org.apache.dubbo.config.AbstractConfig.refresh(AbstractConfig.java:734)
    ...
    at org.apache.dubbo.config.bootstrap.DubboBootstrap.start(DubboBootstrap.java:219)

Note it surfaces during application start, several frames from the config that caused it, with
nothing naming the service or the interface.

Substituting a user-defined wrong interface reports correctly, because its loader is non-null:

java.lang.IllegalStateException: The class ergo.GreeterServiceImpl[classloader=jdk.internal.loader.ClassLoaders$AppClassLoader@746933701]
unimplemented interface ergo.UnrelatedService[classloader=jdk.internal.loader.ClassLoaders$AppClassLoader@746933701]!

That contrast is what narrows the bug to a JDK type being on either side.

What you expected to happen

checkRef should report the misconfiguration it was written to report, regardless of which loader
the classes came from:

The class ergo.GreeterServiceImpl[classloader=jdk.internal.loader.ClassLoaders$AppClassLoader@746933701]
unimplemented interface java.lang.Runnable[classloader=bootstrap]!

That is what a local build of the fix produces for the same main.

Anything else

I kinda added new term bootstrap to the API, so user may be surprised by that. I think if you have a better name, it could be replaced (I was about no-classloader or just default)

Do you have a (mini) reproduction demo?

  • Yes, I have a minimal reproduction demo to help resolve this issue more effectively!

Are you willing to submit a pull request to fix on your own?

  • Yes I am willing to submit a pull request on my own!

Code of Conduct

Activity

  1. LI123456mo commented on Oct 7, 2026

    @LI123456mo
    Contributor

    What about no-classloader? I think it's a bit more self-explanatory than bootstrap for someone who doesn't already know that term — it directly says "there's no classloader object here" rather than requiring the reader to know what 'bootstrap classloader' means.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions