This is an automated email from the ASF dual-hosted git repository.
shunping pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/beam.git
The following commit(s) were added to refs/heads/master by this push:
new 79ae776fca7 Fix PipelineOptions deserialization NPE (#38531)
79ae776fca7 is described below
commit 79ae776fca776ab57be9d8c4c50ffa9ede7e33dd
Author: Shunping Huang <[email protected]>
AuthorDate: Tue May 19 07:55:51 2026 -0400
Fix PipelineOptions deserialization NPE (#38531)
---
.../beam_PreCommit_Python_PVR_Flink.json | 4 ++++
.../beam/sdk/options/PipelineOptionsFactory.java | 26 ++++++++++++++--------
2 files changed, 21 insertions(+), 9 deletions(-)
diff --git a/.github/trigger_files/beam_PreCommit_Python_PVR_Flink.json
b/.github/trigger_files/beam_PreCommit_Python_PVR_Flink.json
new file mode 100644
index 00000000000..616d37428c0
--- /dev/null
+++ b/.github/trigger_files/beam_PreCommit_Python_PVR_Flink.json
@@ -0,0 +1,4 @@
+{
+ "comment": "Modify this file in a trivial way to cause this test suite to
run",
+ "revision": 1
+}
diff --git
a/sdks/java/core/src/main/java/org/apache/beam/sdk/options/PipelineOptionsFactory.java
b/sdks/java/core/src/main/java/org/apache/beam/sdk/options/PipelineOptionsFactory.java
index ac76a57b6b0..b9061e1734c 100644
---
a/sdks/java/core/src/main/java/org/apache/beam/sdk/options/PipelineOptionsFactory.java
+++
b/sdks/java/core/src/main/java/org/apache/beam/sdk/options/PipelineOptionsFactory.java
@@ -502,13 +502,6 @@ public class PipelineOptionsFactory {
new ObjectMapper()
.registerModules(ObjectMapper.findModules(ReflectHelpers.findClassLoader()));
- private static final DefaultDeserializationContext DESERIALIZATION_CONTEXT =
- new
DefaultDeserializationContext.Impl(MAPPER.getDeserializationContext().getFactory())
- .createInstance(
- MAPPER.getDeserializationConfig(),
- new TokenBuffer(MAPPER, false).asParser(),
- new InjectableValues.Std());
-
static final DefaultSerializerProvider SERIALIZER_PROVIDER =
new DefaultSerializerProvider.Impl()
.createInstance(MAPPER.getSerializationConfig(),
MAPPER.getSerializerFactory());
@@ -1733,7 +1726,12 @@ public class PipelineOptionsFactory {
BeanProperty prop = createBeanProperty(method);
AnnotatedMember annotatedMethod = prop.getMember();
- DefaultDeserializationContext context = DESERIALIZATION_CONTEXT.copy();
+ // Initialize a new context that is properly associated with a dummy
parser.
+ // Using copy() here would leave the context's transient parser field as
null,
+ // causing NullPointerExceptions in Jackson 2.14+ when deserializers try
to
+ // query the parser for format constraints or coercion validations.
+ JsonParser dummyParser = new TokenBuffer(MAPPER, false).asParser();
+ DefaultDeserializationContext context =
createDeserializationContext(dummyParser);
Object maybeDeserializerClass =
context.getAnnotationIntrospector().findDeserializer(annotatedMethod);
@@ -1756,6 +1754,11 @@ public class PipelineOptionsFactory {
}
}
+ private static DefaultDeserializationContext
createDeserializationContext(JsonParser parser) {
+ return ((DefaultDeserializationContext) MAPPER.getDeserializationContext())
+ .createInstance(MAPPER.getDeserializationConfig(), parser, new
InjectableValues.Std());
+ }
+
private static Optional<JsonSerializer<Object>>
computeCustomSerializerForMethod(Method method) {
try {
BeanProperty prop = createBeanProperty(method);
@@ -1805,7 +1808,12 @@ public class PipelineOptionsFactory {
parser.nextToken();
JsonDeserializer<Object> jsonDeserializer =
getDeserializerForMethod(method);
- return jsonDeserializer.deserialize(parser,
DESERIALIZATION_CONTEXT.copy());
+ // Create a fresh context that is correctly associated with the active
parser.
+ // Using copy() here would leave the context's transient parser field as
null,
+ // causing NullPointerExceptions in Jackson 2.14+ deserializers during
coercion
+ // validations and length checks.
+ DefaultDeserializationContext context =
createDeserializationContext(parser);
+ return jsonDeserializer.deserialize(parser, context);
}
/**