{"record":{"id":"1f409abb9cd22c35","repo":"apache/beam","slug":"found-incorrectly-annotated-property-methods-if-a-method-is","errorCode":null,"errorMessage":"Found incorrectly annotated property methods, if a method is annotated with either @JsonSerialize or @JsonDeserialize then it must be annotated with both.%n  - Property [%s] had only @%s","messagePattern":"Found incorrectly annotated property methods, if a method is annotated with either @JsonSerialize or @JsonDeserialize then it must be annotated with both\\.%n  - Property \\[(.+?)\\] had only @(.+?)","errorType":"validation","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"sdks/java/core/src/main/java/org/apache/beam/sdk/options/PipelineOptionsFactory.java","lineNumber":1523,"sourceCode":"\n    StringBuilder builder =\n        new StringBuilder(\n            \"Found incorrectly annotated property methods, if a method is annotated with either @JsonSerialize or @JsonDeserialize then it must be annotated with both.\");\n\n    for (InconsistentJsonSerializeAndDeserializeAnnotation annotation : inconsistentAnnotations) {\n      String presentAnnotation;\n      if (annotation.hasJsonDeserializeAttribute) {\n        presentAnnotation = \"JsonDeserialize\";\n      } else {\n        presentAnnotation = \"JsonSerialize\";\n      }\n      builder.append(\n          String.format(\n              \"%n  - Property [%s] had only @%s\",\n              annotation.property.getName(), presentAnnotation));\n    }\n\n    throw new IllegalArgumentException(builder.toString());\n  }\n\n  /** A {@link Comparator} that uses the classes name to compare them. */\n  private static class ClassNameComparator implements Comparator<Class<?>> {\n    static final ClassNameComparator INSTANCE = new ClassNameComparator();\n\n    @Override\n    public int compare(Class<?> o1, Class<?> o2) {\n      return o1.getName().compareTo(o2.getName());\n    }\n  }\n\n  /** A {@link Comparator} that uses the generic method signature to sort them. */\n  private static class MethodComparator implements Comparator<Method> {\n    static final MethodComparator INSTANCE = new MethodComparator();\n\n    @Override\n    public int compare(Method o1, Method o2) {","sourceCodeStart":1505,"sourceCodeEnd":1541,"githubUrl":"https://github.com/apache/beam/blob/12126d8942aaf848030c478b4c6a28c6af861c66/sdks/java/core/src/main/java/org/apache/beam/sdk/options/PipelineOptionsFactory.java#L1505-L1541","documentation":"PipelineOptions validation found a getter method annotated with exactly one of @JsonSerialize or @JsonDeserialize. Beam requires both annotations together so serialization and deserialization behavior stay symmetric; a one-sided annotation is considered a programming error in the PipelineOptions interface.","triggerScenarios":"Declaring a PipelineOptions interface method with only @JsonSerialize (without @JsonDeserialize) or only @JsonDeserialize (without @JsonSerialize), then calling PipelineOptionsFactory.create()/as()/fromArgs() which triggers property annotation validation.","commonSituations":"Adding custom Jackson (de)serialization to a value-typed option and forgetting the matching counterpart annotation; copying an annotation from an example without both.","solutions":["Add the missing counterpart annotation to the getter so both @JsonSerialize and @JsonDeserialize are present","If only one direction is needed, remove the single annotation entirely and rely on default Jackson behavior"],"exampleFix":"// before\n@JsonSerialize(using = MySerializer.class)\nMyValue getMyOption();\n// after\n@JsonSerialize(using = MySerializer.class)\n@JsonDeserialize(using = MyDeserializer.class)\nMyValue getMyOption();","handlingStrategy":"validation","validationCode":"for (Method m : MyOptions.class.getMethods()) {\n  boolean s = m.isAnnotationPresent(JsonSerialize.class);\n  boolean d = m.isAnnotationPresent(JsonDeserialize.class);\n  if (s != d) throw new IllegalStateException(m.getName() + \" needs both @JsonSerialize and @JsonDeserialize\");\n}","typeGuard":null,"tryCatchPattern":"try { PipelineOptionsFactory.as(MyOptions.class); } catch (IllegalArgumentException e) { /* inspect annotation pair error */ }","preventionTips":["Always add both annotations as a pair when customizing Jackson behavior","Add an ArchUnit or annotation-checking unit test over your options interfaces"],"tags":["java","apache-beam","annotations","json","pipeline-options"],"backgroundTag":"invalid-argument-value","analyzedSha":"12126d8942aaf848030c478b4c6a28c6af861c66","analyzedAt":"2026-09-13T01:50:10.254Z","contentChangedAt":"2026-09-13T01:50:10.254Z","schemaVersion":2},"datasetVersion":"2026-09-14T16:17:12.679Z"}