apache/beam · error · IllegalArgumentException

Only Put and Delete are supported

Error message

Only Put and Delete are supported

What it means

HBaseMutationCoder only supports idempotent mutation types: Put and Delete. Increment and Append are non-idempotent and unsafe for distributed retries, so the coder's getType method throws IllegalArgumentException for any other Mutation subclass.

Solutions

  1. Replace Increment/Append with Put operations (compute the new value client-side) to keep idempotency.
  2. If Delete/Put only logic is required, filter mutations before writing: only pass instanceof Put or Delete.
  3. Use a different sink or a custom coder path if you genuinely need Increment/Append semantics.

Example fix

// before
list.add(new Increment(row, CF, QUAL, 1L));
// after — read current value, then Put
long v = readCurrentValue(row, CF, QUAL);
list.add(new Put(row).addColumn(CF, QUAL, Bytes.toBytes(v + 1)));
Defensive patterns

Strategy: type-guard

Validate before calling

public static List<Mutation> onlyIdempotent(List<Mutation> mutations) {
  return mutations.stream()
      .filter(m -> m instanceof Put || m instanceof Delete)
      .collect(Collectors.toList());
}

Type guard

static boolean isSupported(Mutation m) {
  return m instanceof Put || m instanceof Delete;
}

Try / catch

try {
  encodeMutation(mutation);
} catch (IllegalArgumentException e) {
  if (e.getMessage().contains("Only Put and Delete")) {
    throw new IllegalStateException("Replace Increment/Append with idempotent Put", e);
  }
  throw e;
}

Prevention

When it happens

Trigger: Encoding/decoding or coder lookup for a Mutation that is an Increment or Append (or any custom Mutation subclass) inside a pipeline using HBaseMutationCoder — typically writing such mutations with HBaseIO or passing them through Coders.defaultCoder resolution.

Common situations: Using Increment/Append operations in a Beam HBaseIO pipeline; a custom Mutation subclass; library version change introducing new Mutation types.

Related errors


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

Appendix: source

Thrown at sdks/java/io/hbase/src/main/java/org/apache/beam/sdk/io/hbase/HBaseMutationCoder.java:69

  public void encode(Mutation mutation, OutputStream outStream) throws IOException {
    MutationType type = getType(mutation);
    MutationProto proto = ProtobufUtil.toMutation(type, mutation);
    proto.writeDelimitedTo(outStream);
  }

  @Override
  public Mutation decode(InputStream inStream) throws IOException {
    return ProtobufUtil.toMutation(MutationProto.parseDelimitedFrom(inStream));
  }

  private static MutationType getType(Mutation mutation) {
    if (mutation instanceof Put) {
      return MutationType.PUT;
    } else if (mutation instanceof Delete) {
      return MutationType.DELETE;
    } else {
      // Increment and Append are not idempotent.  They should not be used in distributed jobs.
      throw new IllegalArgumentException("Only Put and Delete are supported");
    }
  }

  /**
   * Returns a {@link CoderProvider} which uses the {@link HBaseMutationCoder} for {@link Mutation
   * mutations}.
   */
  static CoderProvider getCoderProvider() {
    return HBASE_MUTATION_CODER_PROVIDER;
  }

  private static final CoderProvider HBASE_MUTATION_CODER_PROVIDER =
      new HBaseMutationCoderProvider();

  /** A {@link CoderProvider} for {@link Mutation mutations}. */
  private static class HBaseMutationCoderProvider extends CoderProvider {
    @Override
    public <T> Coder<T> coderFor(

View on GitHub (pinned to 12126d8942)