apache/beam · error · IllegalArgumentException

Found incorrectly annotated property methods, if a method is

Error message

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

What it means

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.

Source

Thrown at sdks/java/core/src/main/java/org/apache/beam/sdk/options/PipelineOptionsFactory.java:1523

    StringBuilder builder =
        new StringBuilder(
            "Found incorrectly annotated property methods, if a method is annotated with either @JsonSerialize or @JsonDeserialize then it must be annotated with both.");

    for (InconsistentJsonSerializeAndDeserializeAnnotation annotation : inconsistentAnnotations) {
      String presentAnnotation;
      if (annotation.hasJsonDeserializeAttribute) {
        presentAnnotation = "JsonDeserialize";
      } else {
        presentAnnotation = "JsonSerialize";
      }
      builder.append(
          String.format(
              "%n  - Property [%s] had only @%s",
              annotation.property.getName(), presentAnnotation));
    }

    throw new IllegalArgumentException(builder.toString());
  }

  /** A {@link Comparator} that uses the classes name to compare them. */
  private static class ClassNameComparator implements Comparator<Class<?>> {
    static final ClassNameComparator INSTANCE = new ClassNameComparator();

    @Override
    public int compare(Class<?> o1, Class<?> o2) {
      return o1.getName().compareTo(o2.getName());
    }
  }

  /** A {@link Comparator} that uses the generic method signature to sort them. */
  private static class MethodComparator implements Comparator<Method> {
    static final MethodComparator INSTANCE = new MethodComparator();

    @Override
    public int compare(Method o1, Method o2) {

View on GitHub (pinned to 12126d8942)

Solutions

  1. Add the missing counterpart annotation to the getter so both @JsonSerialize and @JsonDeserialize are present
  2. If only one direction is needed, remove the single annotation entirely and rely on default Jackson behavior

Example fix

// before
@JsonSerialize(using = MySerializer.class)
MyValue getMyOption();
// after
@JsonSerialize(using = MySerializer.class)
@JsonDeserialize(using = MyDeserializer.class)
MyValue getMyOption();
Defensive patterns

Strategy: validation

Validate before calling

for (Method m : MyOptions.class.getMethods()) {
  boolean s = m.isAnnotationPresent(JsonSerialize.class);
  boolean d = m.isAnnotationPresent(JsonDeserialize.class);
  if (s != d) throw new IllegalStateException(m.getName() + " needs both @JsonSerialize and @JsonDeserialize");
}

Try / catch

try { PipelineOptionsFactory.as(MyOptions.class); } catch (IllegalArgumentException e) { /* inspect annotation pair error */ }

Prevention

When it happens

Trigger: 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.

Common situations: Adding custom Jackson (de)serialization to a value-typed option and forgetting the matching counterpart annotation; copying an annotation from an example without both.

Understand the failure class

Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.

Related errors


AI-assisted analysis of apache/beam@12126d8942 (2026-09-13). Data as JSON: /api/errors/1f409abb9cd22c35. Report an issue: GitHub.