{"record":{"id":"20e4d40a6843cb00","repo":"gradle/gradle","slug":"cannot-load-worker-action-s-class","errorCode":null,"errorMessage":"Cannot load worker action's class","messagePattern":"Cannot load worker action's class","errorType":"exception","errorClass":"RuntimeException","httpStatus":null,"severity":"error","filePath":"platforms/core-execution/worker-main/src/main/java/org/gradle/process/internal/worker/messaging/WorkerConfigSerializer.java","lineNumber":93,"sourceCode":"        encoder.writeSmallInt(config.getNativeServicesMode().ordinal());\n        encoder.writeString(config.getGradleUserHomeDirPath());\n        new MultiChoiceAddressSerializer().write(encoder, config.getServerAddress());\n        encoder.writeSmallLong(config.getWorkerId());\n        encoder.writeString(config.getDisplayName());\n        encoder.writeBinary(serializeWorker(config.getWorkerAction()));\n    }\n\n    private static Action<? super WorkerProcessContext> deserializeWorker(byte[] serializedWorker, ClassLoader loader) throws IOException {\n        ByteArrayInputStream bais = new ByteArrayInputStream(serializedWorker);\n        ObjectInputStream in = null;\n        try {\n            in = new ClassLoaderObjectInputStream(bais, loader);\n\n            @SuppressWarnings(\"unchecked\")\n            Action<? super WorkerProcessContext> workerAction = (Action<? super WorkerProcessContext>) in.readObject();\n            return workerAction;\n        } catch (ClassNotFoundException e) {\n            throw new RuntimeException(\"Cannot load worker action's class\", e);\n        } catch (UnsupportedClassVersionError e) {\n            String message;\n            if (e instanceof UnsupportedClassVersionErrorWithJavaVersion) {\n                UnsupportedClassVersionErrorWithJavaVersion e2 = (UnsupportedClassVersionErrorWithJavaVersion) e;\n                message = String.format(\n                    \"Unsupported worker JDK version. Required: %s. Current: %s\",\n                    e2.getVersion().getMajorVersion(), JavaVersion.current().getMajorVersion()\n                );\n            } else {\n                message = \"Unsupported worker JDK version: \" + JavaVersion.current().getMajorVersion();\n            }\n            throw new GradleException(message, e);\n        } finally {\n            if (in != null) {\n                try {\n                    in.close();\n                } catch (IOException e) {\n                    LOGGER.debug(\"Error closing ObjectInputStream\", e);","sourceCodeStart":75,"sourceCodeEnd":111,"githubUrl":"https://github.com/gradle/gradle/blob/534f27719b66953f95cc907aae7f2c1b12f5482d/platforms/core-execution/worker-main/src/main/java/org/gradle/process/internal/worker/messaging/WorkerConfigSerializer.java#L75-L111","documentation":"Worker actions are shipped to the child JVM as serialized objects and read back with a ClassLoaderObjectInputStream bound to the worker's classloader. ClassNotFoundException during readObject is rethrown as this RuntimeException: the action's class, or a type it references, is absent from the classloader the worker process was configured with. The cause names the missing class.","triggerScenarios":"Deserializing the worker action in the child when the classpath given to the worker process (implementation classpath) does not include every jar holding the action and the types it references.","commonSituations":"Custom worker process implementations with incomplete classpath entries, stale or republished plugin jars after an upgrade, shaded/relocated classes crossing the worker boundary, version drift between serializer and worker classloader.","solutions":["Read the cause to get the missing class name, then locate which jar should provide it","Add that jar to the worker's implementation classpath (setImplementationClasspath / workerImplementation)","Rebuild and republish plugin jars so both sides of the worker boundary match","Do not shade or relocate classes that cross the process boundary"],"exampleFix":"// before\nbuilder.setImplementationClasspath(Arrays.asList(gradleApiJar));\n// action class com.acme.MyWorkerAction lives in acme-worker.jar -> ClassNotFoundException\n\n// after\nbuilder.setImplementationClasspath(Arrays.asList(gradleApiJar, acmeWorkerJar));","handlingStrategy":"validation","validationCode":"try {\n    Class.forName(\"com.acme.MyWorkerAction\", false, workerClassLoader);\n} catch (ClassNotFoundException e) {\n    // add the missing jar to the worker implementation classpath before starting\n    throw new IllegalStateException(\"Worker action class missing from worker classpath\", e);\n}","typeGuard":null,"tryCatchPattern":"try {\n    action = deserializeWorker(bytes, loader);\n} catch (RuntimeException e) {\n    if (e.getCause() instanceof ClassNotFoundException) {\n        // named class is absent: fix the implementation classpath, do not retry blindly\n    } else {\n        throw e;\n    }\n}","preventionTips":["Derive the worker classpath from the same jars that built the serialized action","Do not shade or relocate classes that cross the worker boundary","Rebuild both sides together when plugin jars change"],"tags":["gradle","worker-process","serialization","classpath"],"backgroundTag":"classnotfoundexception","analyzedSha":"534f27719b66953f95cc907aae7f2c1b12f5482d","analyzedAt":"2026-08-22T08:09:12.375Z","schemaVersion":2},"datasetVersion":"2026-08-22T09:17:25.309Z"}