{"record":{"id":"2c7b76d36d87f579","repo":"Tencent/tinker","slug":"cannot-find-class","errorCode":null,"errorMessage":"cannot find class: {}","messagePattern":"cannot find class: (.+?)","errorType":"exception","errorClass":"ClassNotFoundException","httpStatus":null,"severity":"error","filePath":"tinker-android/tinker-android-loader/src/main/java/com/tencent/tinker/loader/hotplug/interceptor/ServiceBinderInterceptor.java","lineNumber":176,"sourceCode":"                cl = uniqueCls.iterator().next();\n            } else {\n                cl = new ClassLoader() {\n                    @Override\n                    protected Class<?> loadClass(String className, boolean resolve)\n                            throws ClassNotFoundException {\n                        Class<?> res = null;\n                        for (ClassLoader cl : uniqueCls) {\n                            try {\n                                // fix some device PathClassLoader behind BootClassLoader which lead to ClassNotFoundException\n                                res = cl.loadClass(className);\n                            } catch (Throwable ignore) {\n\n                            }\n                            if (res != null) {\n                                return res;\n                            }\n                        }\n                        throw new ClassNotFoundException(\"cannot find class: \" + className);\n                    }\n                };\n            }\n            try {\n                return (T) Proxy.newProxyInstance(cl, mergedItfs, handler);\n            } catch (Throwable thr2) {\n                throw new RuntimeException(\"cl: \" + cl, thr);\n            }\n        }\n    }\n\n    private static Class<?>[] getAllInterfacesThroughDeriveChain(Class<?> clazz) {\n        if (clazz == null) {\n            return null;\n        }\n        final Set<Class<?>> itfs = new HashSet<>(10);\n        while (!Object.class.equals(clazz)) {\n            itfs.addAll(Arrays.asList(clazz.getInterfaces()));","sourceCodeStart":158,"sourceCodeEnd":194,"githubUrl":"https://github.com/Tencent/tinker/blob/1b7ea02c239840f563ea64fb5bd286eb98d4011e/tinker-android/tinker-android-loader/src/main/java/com/tencent/tinker/loader/hotplug/interceptor/ServiceBinderInterceptor.java#L158-L194","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Verify the class named in the exception actually exists in the installed base APK or patch dex (use dexdump / apktool on both APKs).","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.","Ensure patch and base APK apply identical proguard mappings so class names resolve consistently.","On affected OEM devices, avoid the component-hotplug proxy path or update Tinker, since the multi-classloader loop already works around BootClassLoader ordering issues.","Catch ClassNotFoundException at the proxy boundary and degrade gracefully (skip hotplug for that call) instead of crashing the process."],"exampleFix":"// before\nres = cl.loadClass(className); // last loop iteration, res stays null\n// after\ntry {\n    res = cl.loadClass(className);\n} catch (Throwable ignore) {\n    // try next classloader\n}\nif (res == null && isOptionalIpcClass(className)) {\n    return Object.class; // or skip argument handling\n}","handlingStrategy":"try-catch","validationCode":"// Before enabling component hotplug proxying, ensure the classes it will reference exist\nClass<?> probe = null;\nfor (ClassLoader cl : new ClassLoader[]{ app.getClassLoader(), ClassLoader.getSystemClassLoader() }) {\n    try { probe = cl.loadClass(\"com.example.patched.ParcelableArg\"); break; } catch (Throwable ignore) {}\n}\nif (probe == null) { /* do not enable proxy for this call */ }","typeGuard":null,"tryCatchPattern":"try {\n    Object proxy = ServiceBinderInterceptor.proxyServiceManagement(...);\n} catch (ClassNotFoundException cnfe) {\n    // class missing from all merged classloaders: skip hotplug proxy, keep default binder\n} catch (RuntimeException re) {\n    Throwable cause = re.getCause(); // original failure chained by the interceptor\n}","preventionTips":["Keep proguard mappings identical between base APK and patch.","Do not pass Parcelable types that only exist in a side-loaded dex through proxied binder calls.","Test component hotplug on the OEM ROMs you ship to, especially those with unusual classloader hierarchies."],"tags":["android","tinker","classloader","proxy","ipc","hotplug"],"backgroundTag":null,"analyzedSha":"1b7ea02c239840f563ea64fb5bd286eb98d4011e","analyzedAt":"2026-08-14T15:16:52.110Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}