apache/beam · error · RuntimeException

Malformed class : state declaration field is not accessible.

Error message

Malformed %s class %s: state declaration field %s is not accessible.

What it means

Beam reads a DoFn's @StateId-decorated field reflectively to get its StateSpec. If Field.get(target) throws IllegalAccessException (the field is not public or otherwise inaccessible), Beam wraps it in a RuntimeException stating the DoFn class is malformed. This indicates a state declaration field that violates Beam's accessibility requirement.

Solutions

  1. Make the @StateId-annotated field public (Beam requires state declaration fields to be public)
  2. Ensure the field is an instance field on the DoFn class itself, not inherited from an inaccessible superclass
  3. Confirm the field type is StateSpec<...> with a @StateId annotation
  4. If using a superclass DoFn, expose the field publicly there or restructure so the subclass declares it

Example fix

// before
@StateId("seen")
private final StateSpec<ValueState<Integer>> seenSpec = StateSpecs.value();

// after
@StateId("seen")
public final StateSpec<ValueState<Integer>> seenSpec = StateSpecs.value();
Defensive patterns

Strategy: validation

Validate before calling

// Verify @StateId fields are public before building the pipeline
for (Field f : MyFn.class.getDeclaredFields()) {
  if (f.isAnnotationPresent(StateId.class) && !Modifier.isPublic(f.getModifiers())) {
    throw new IllegalStateException("@StateId field must be public: " + f.getName());
  }
}

Type guard

static boolean isAccessibleStateField(Field f) {
  return Modifier.isPublic(f.getModifiers())
      && StateSpec.class.isAssignableFrom(f.getType());

Try / catch

try {
  pipeline.run();
} catch (RuntimeException e) {
  if (e.getMessage().contains("state declaration field")) {
    // make the field public and retry
  }
}

Prevention

When it happens

Trigger: Using @StateId on a field that is private/protected/package-private (or in a non-public class with restricted access), so reflection via stateDeclaration.field().get(target) fails with IllegalAccessException while retrieving the StateSpec.

Common situations: Developers habitually declare fields private; refactoring moves a DoFn into another class/package and field access rules tighten; annotation-processor-less environments where a private @StateId field was never validated at compile time.

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/700a6f89b79ca642. Report an issue: GitHub.

Appendix: source

Thrown at sdks/java/core/src/main/java/org/apache/beam/sdk/transforms/reflect/DoFnSignatures.java:2534

      }
    }
  }

  public static StateSpec<?> getStateSpecOrThrow(
      StateDeclaration stateDeclaration, DoFn<?, ?> target) {
    try {
      Object fieldValue = stateDeclaration.field().get(target);
      checkState(
          fieldValue instanceof StateSpec,
          "Malformed %s class %s: state declaration field %s does not have type %s.",
          format(DoFn.class),
          target.getClass().getName(),
          stateDeclaration.field().getName(),
          StateSpec.class);

      return (StateSpec<?>) stateDeclaration.field().get(target);
    } catch (IllegalAccessException exc) {
      throw new RuntimeException(
          String.format(
              "Malformed %s class %s: state declaration field %s is not accessible.",
              format(DoFn.class), target.getClass().getName(), stateDeclaration.field().getName()));
    }
  }

  public static TimerSpec getTimerSpecOrThrow(
      TimerDeclaration timerDeclaration, DoFn<?, ?> target) {
    try {
      Object fieldValue = timerDeclaration.field().get(target);
      checkState(
          fieldValue instanceof TimerSpec,
          "Malformed %s class %s: timer declaration field %s does not have type %s.",
          format(DoFn.class),
          target.getClass().getName(),
          timerDeclaration.field().getName(),
          TimerSpec.class);

View on GitHub (pinned to 12126d8942)