apache/iceberg · error · IllegalArgumentException

Cannot convert nested accessor to position

Error message

Cannot convert nested accessor to position

What it means

Accessors.toPosition() converts an accessor to an integer position only when it is a PositionAccessor. Nested accessors (e.g. accessing a field inside a nested struct) cannot be reduced to a single top-level position, so this IllegalArgumentException is thrown. It guards against misuse of the accessor API.

Source

Thrown at api/src/main/java/org/apache/iceberg/Accessors.java:51

 *  |-- a: struct (nullable = false)
 *  |    |-- b: struct (nullable = false)
 *  |        | -- c: string (containsNull = false)
 * </pre>
 *
 * Then we will use Position3Accessor to access nested field 'c'. It can be accessed like this:
 * {@code row.get(p0, StructLike.class).get(p1, StructLike.class).get(p2, javaClass)}. Commonly,
 * Nested fields with depth=1 or 2 or 3 are the fields that will be accessed frequently, so this
 * optimization will help to access this kind of schema. For schema whose depth is deeper than 3,
 * then we will use the {@link WrappedPositionAccessor} to access recursively.
 */
public class Accessors {
  private Accessors() {}

  public static Integer toPosition(Accessor<StructLike> accessor) {
    if (accessor instanceof PositionAccessor) {
      return ((PositionAccessor) accessor).position();
    }
    throw new IllegalArgumentException("Cannot convert nested accessor to position");
  }

  static Map<Integer, Accessor<StructLike>> forSchema(Schema schema) {
    return TypeUtil.visit(schema, new BuildPositionAccessors());
  }

  private static class PositionAccessor implements Accessor<StructLike> {
    private final int position;
    private final Type type;
    private final Class<?> javaClass;

    PositionAccessor(int pos, Type type) {
      this.position = pos;
      this.type = type;
      this.javaClass = type.typeId().javaClass();
    }

    @Override

View on GitHub (pinned to 86d9c8fc54)

Solutions

  1. Only call toPosition() for top-level (non-nested) field accessors; check instanceof Accessors.PositionAccessor first.
  2. For nested fields, navigate the accessor chain yourself instead of asking for a single position.
  3. If your schema must be flat, flatten it (e.g. schema case-aware access via projected schema) before building accessors.

Example fix

// before
Integer pos = Accessors.toPosition(accessor);

// after
if (accessor instanceof Accessors.PositionAccessor) {
  Integer pos = ((Accessors.PositionAccessor) accessor).position();
} else {
  // handle nested accessor path
}
Defensive patterns

Strategy: type-guard

Type guard

if (accessor instanceof Accessors.PositionAccessor) {
  int pos = ((Accessors.PositionAccessor) accessor).position();
} else {
  // nested accessor: walk its nested path instead of toPosition()
}

Try / catch

try {
  Integer pos = Accessors.toPosition(accessor);
} catch (IllegalArgumentException e) {
  // fall back to nested-accessor handling
}

Prevention

When it happens

Trigger: Passing an accessor obtained for a nested field (produced by visiting a schema with nested struct fields) into Accessors.toPosition(), e.g. when code assumes all accessors are positional.

Common situations: Writing generic code over schema accessors that handles flat schemas but encounters nested types (struct-in-struct, list of structs); older code paths after schemas gained nested fields.

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/iceberg@86d9c8fc54 (2026-09-12). Data as JSON: /api/errors/80f9f8f1a03c277c. Report an issue: GitHub.