Tencent/tinker · error · ClassNotFoundException

cannot find class: {}

Error message

cannot find class: {}

What it means

Thrown by Tinker's ServiceBinderInterceptor when a dynamic Proxy invocation handler cannot load a class named in an IPC method invocation from any of the merged classloaders (patch PathClassLoader plus BootClassLoader). The interceptor proxies framework binder interfaces (e.g. IServiceManager, IActivityManager) so that parceled arguments coming from the patched app can be resolved. If a class referenced by the call is only present in a classloader not reachable from the merged set, ClassNotFoundException is thrown after all candidate loaders fail.

Source

Thrown at tinker-android/tinker-android-loader/src/main/java/com/tencent/tinker/loader/hotplug/interceptor/ServiceBinderInterceptor.java:176

                cl = uniqueCls.iterator().next();
            } else {
                cl = new ClassLoader() {
                    @Override
                    protected Class<?> loadClass(String className, boolean resolve)
                            throws ClassNotFoundException {
                        Class<?> res = null;
                        for (ClassLoader cl : uniqueCls) {
                            try {
                                // fix some device PathClassLoader behind BootClassLoader which lead to ClassNotFoundException
                                res = cl.loadClass(className);
                            } catch (Throwable ignore) {

                            }
                            if (res != null) {
                                return res;
                            }
                        }
                        throw new ClassNotFoundException("cannot find class: " + className);
                    }
                };
            }
            try {
                return (T) Proxy.newProxyInstance(cl, mergedItfs, handler);
            } catch (Throwable thr2) {
                throw new RuntimeException("cl: " + cl, thr);
            }
        }
    }

    private static Class<?>[] getAllInterfacesThroughDeriveChain(Class<?> clazz) {
        if (clazz == null) {
            return null;
        }
        final Set<Class<?>> itfs = new HashSet<>(10);
        while (!Object.class.equals(clazz)) {
            itfs.addAll(Arrays.asList(clazz.getInterfaces()));

View on GitHub (pinned to 1b7ea02c23)

Solutions

  1. Verify the class named in the exception actually exists in the installed base APK or patch dex (use dexdump / apktool on both APKs).
  2. If the class lives in a separate dynamic feature or plugin classloader, add that classloader to the interceptor's merged set (uniqueCls) before the proxy is created.
  3. Ensure patch and base APK apply identical proguard mappings so class names resolve consistently.
  4. On affected OEM devices, avoid the component-hotplug proxy path or update Tinker, since the multi-classloader loop already works around BootClassLoader ordering issues.
  5. Catch ClassNotFoundException at the proxy boundary and degrade gracefully (skip hotplug for that call) instead of crashing the process.

Example fix

// before
res = cl.loadClass(className); // last loop iteration, res stays null
// after
try {
    res = cl.loadClass(className);
} catch (Throwable ignore) {
    // try next classloader
}
if (res == null && isOptionalIpcClass(className)) {
    return Object.class; // or skip argument handling
}
Defensive patterns

Strategy: try-catch

Validate before calling

// Before enabling component hotplug proxying, ensure the classes it will reference exist
Class<?> probe = null;
for (ClassLoader cl : new ClassLoader[]{ app.getClassLoader(), ClassLoader.getSystemClassLoader() }) {
    try { probe = cl.loadClass("com.example.patched.ParcelableArg"); break; } catch (Throwable ignore) {}
}
if (probe == null) { /* do not enable proxy for this call */ }

Try / catch

try {
    Object proxy = ServiceBinderInterceptor.proxyServiceManagement(...);
} catch (ClassNotFoundException cnfe) {
    // class missing from all merged classloaders: skip hotplug proxy, keep default binder
} catch (RuntimeException re) {
    Throwable cause = re.getCause(); // original failure chained by the interceptor
}

Prevention

When it happens

Trigger: Any proxied binder call whose Parcel contains a class name that resolves in none of the collected classloaders: cl.loadClass(className) returns null / throws for every entry of uniqueCls, then 'throw new ClassNotFoundException("cannot find class: " + className)' fires inside the InvocationHandler.

Common situations: Using Tinker's component hotplug (proxy running Services) on OEM ROMs where the PathClassLoader sits behind the BootClassLoader; a patch that references a class removed or renamed between patch and base APK; cross-process IPC passing custom Parcelable types loaded by a plugin/second dex set; obfuscated or missing classes after aggressive proguard on the patch.

Related errors


AI-assisted analysis of Tencent/tinker@1b7ea02c23 (2026-08-14). Data as JSON: /api/errors/2c7b76d36d87f579. Report an issue: GitHub.