From 278654cfd32d0230fbe8cb201e9c668aa3093706 Mon Sep 17 00:00:00 2001 From: Xi Chen Date: Sat, 31 Aug 2024 14:47:55 +0800 Subject: [PATCH] [SPARK-49480][CORE] Fix NullPointerException from `SparkThrowableHelper.isInternalError` The SparkThrowableHelper.isInternalError method doesn't handle null input, and it could lead to NullPointerException. Example stacktrace from our environment with Spark 3.5.1: ``` Caused by: java.lang.NullPointerException: Cannot invoke "String.startsWith(String)" because "errorClass" is null at org.apache.spark.SparkThrowableHelper$.isInternalError(SparkThrowableHelper.scala:64) at org.apache.spark.SparkThrowableHelper.isInternalError(SparkThrowableHelper.scala) at org.apache.spark.SparkThrowable.isInternalError(SparkThrowable.java:50) at org.apache.spark.SparkException.isInternalError(SparkException.scala:27) at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77) at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.base/java.lang.reflect.Method.invoke(Method.java:568) at com.fasterxml.jackson.databind.ser.BeanPropertyWriter.serializeAsField(BeanPropertyWriter.java:688) at com.fasterxml.jackson.databind.ser.std.BeanSerializerBase.serializeFields(BeanSerializerBase.java:772) ... 30 more ``` --- .../src/main/scala/org/apache/spark/SparkThrowableHelper.scala | 2 +- core/src/test/scala/org/apache/spark/SparkThrowableSuite.scala | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/common/utils/src/main/scala/org/apache/spark/SparkThrowableHelper.scala b/common/utils/src/main/scala/org/apache/spark/SparkThrowableHelper.scala index db5eff72e124..428c9d2a4935 100644 --- a/common/utils/src/main/scala/org/apache/spark/SparkThrowableHelper.scala +++ b/common/utils/src/main/scala/org/apache/spark/SparkThrowableHelper.scala @@ -74,7 +74,7 @@ private[spark] object SparkThrowableHelper { } def isInternalError(errorClass: String): Boolean = { - errorClass.startsWith("INTERNAL_ERROR") + errorClass != null && errorClass.startsWith("INTERNAL_ERROR") } def getMessage(e: SparkThrowable with Throwable, format: ErrorMessageFormat.Value): String = { diff --git a/core/src/test/scala/org/apache/spark/SparkThrowableSuite.scala b/core/src/test/scala/org/apache/spark/SparkThrowableSuite.scala index 0c22edbe984c..d99589c171c3 100644 --- a/core/src/test/scala/org/apache/spark/SparkThrowableSuite.scala +++ b/core/src/test/scala/org/apache/spark/SparkThrowableSuite.scala @@ -259,6 +259,7 @@ class SparkThrowableSuite extends SparkFunSuite { } catch { case e: SparkThrowable => assert(e.getErrorClass == null) + assert(!e.isInternalError) assert(e.getSqlState == null) case _: Throwable => // Should not end up here @@ -275,6 +276,7 @@ class SparkThrowableSuite extends SparkFunSuite { } catch { case e: SparkThrowable => assert(e.getErrorClass == "CANNOT_PARSE_DECIMAL") + assert(!e.isInternalError) assert(e.getSqlState == "22018") case _: Throwable => // Should not end up here