Spark job throws ClassNotFoundException for custom class when KryoSerializer enabled
0 reputation · 02 Dec 2025, 03:08 UTC
0 reputation · 02 Dec 2025, 03:08 UTC
Goal: Ensure that a user‑defined class used in a Spark transformation is serializable when the application runs on a cluster with spark.serializer set to KryoSerializer.
In local mode the job completes because Spark falls back to the JVM’s default Java serializer for driver‑side objects, masking the missing Kryo registration. When submitted to a cluster the same code fails with a ClassNotFoundException for the custom class, indicating that Kryo cannot locate the class without explicit registration. The uncertainty lies in whether to register the class via spark.kryo.classesToRegister, to set spark.kryo.registrationRequired=false to suppress the check, or to rely on alternative serializers, considering version‑specific enforcement and potential performance impacts.
Should the custom class be added to spark.kryo.classesToRegister? Is disabling registrationRequired acceptable for production workloads? What performance trade‑offs arise from each option?
34775 reputation · 02 Dec 2025, 12:37 UTC
The ClassNotFoundException is thrown because the executor’s classloader cannot locate the user‑defined class when KryoSerializer is in use. The fix is to make the class available on the executors and, in most production workloads, register it with Kryo.
When spark.serializer is set to KryoSerializer, Spark serializes all objects that cross the driver‑executor boundary with Kryo. Kryo requires that every class used in a transformation be either (a) registered in spark.kryo.classesToRegister or (b) discoverable on the executor classpath. If the class is missing, Kryo cannot instantiate a ClassResolver for it and throws ClassNotFoundException. In local mode the JVM’s default serializer is used for driver‑side objects, so the missing registration is hidden.
--jars /path/to/your.jar or publish the JAR to a Maven repository and reference it with --packages.spark.kryo.classesToRegister (via SparkConf, spark-defaults.conf, or the command line). This gives Kryo a numeric ID for the class, eliminating the need for reflection and reducing payload size.registrationRequired=false in production. Turning off the check removes a safety net that would surface accidental use of non‑serializable classes and forces Kryo to fall back to slower, larger reflection‑based serialization.| Option | Serialization Size | Speed | Safety |
|---|---|---|---|
| Registered class | Small (numeric ID) | Fast | High – errors surface early |
Unregistered with registrationRequired=false | Large (class name + data) | Slow | Low – may hide bugs |
spark-submit --jars path/to/your.jar \
--conf spark.kryo.classesToRegister=com.example.MyClass \
--conf spark.serializer=org.apache.spark.serializer.KryoSerializer \
yourApp.jar.
Class com.example.MyClass loaded by executor classloader.
Storage tab to confirm that the RDD partitions have a smaller serialized size after registration.
spark.kryo.registrationRequired=false, compare the execution time and executor memory usage against the registered case to quantify the penalty.
Do you ship the JAR containing com.example.MyClass to the cluster executors (via --jars or a repository)? If not, that is the most likely cause of the exception.
Use comments to ask for clarification. Post a solution as an answer.
No question comments on this page.