apache/beam · error · UnsupportedOperationException

RecordOffset unsupported in %s

Error message

RecordOffset unsupported in %s

What it means

This is thrown by DoFnInvoker's UnsupportedInvocationBehavior when a DoFn callback calls currentRecordOffset() and the invoking context cannot supply record offsets. Record offsets exist only in splittable-DoFn element processing against an offset-based restriction; default invocation behavior deliberately rejects such calls rather than returning a bogus value. The message's %s names the error context (DoFn class and callback) so you can see which callback misused the accessor.

Source

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

      throw new UnsupportedOperationException(
          String.format("Timestamp unsupported in %s", getErrorContext()));
    }

    @Override
    public @Nullable String currentRecordId(DoFn<InputT, OutputT> doFn) {
      throw new UnsupportedOperationException(
          String.format("RecordId unsupported in %s", getErrorContext()));
    }

    @Override
    public @Nullable Long currentRecordOffset(DoFn<InputT, OutputT> doFn) {
      throw new UnsupportedOperationException(
          String.format("RecordOffset unsupported in %s", getErrorContext()));
    }

    @Override
    public Instant fireTimestamp(DoFn<InputT, OutputT> doFn) {
      throw new UnsupportedOperationException(
          String.format("FireTimestamp unsupported in %s", getErrorContext()));
    }

    @Override
    public CausedByDrain causedByDrain(DoFn<InputT, OutputT> doFn) {
      throw new UnsupportedOperationException(
          String.format("CausedByDrain unsupported in %s", getErrorContext()));
    }

    @Override
    public ValueKind valueKind(DoFn<InputT, OutputT> doFn) {
      throw new UnsupportedOperationException(
          String.format("ValueKind unsupported in %s", getErrorContext()));
    }

    @Override
    public String timerId(DoFn<InputT, OutputT> doFn) {
      throw new UnsupportedOperationException(

View on GitHub (pinned to 12126d8942)

Solutions

  1. Delete the currentRecordOffset() call or replace it with tracking inside your own restriction tracker
  2. If you truly need offsets, implement the DoFn as a splittable DoFn and run it through a runner path that uses the SDF DoFnRunner, not the default invoker
  3. In tests, use DoFnTestUtils.runFunction or TestPipeline so bundle-data-capable runners drive the DoFn
  4. Catch UnsupportedOperationException around the accessor and degrade gracefully if offsets are optional

Example fix

// before
long offset = context.currentRecordOffset();
// after
// track progress via your RestrictionTracker instead
Progress p = restrictionTracker.getProgress();
Defensive patterns

Strategy: try-catch

Validate before calling

// Ensure the callback is only invoked by a bundle-data-capable runner path,
// e.g. run through TestPipeline instead of a hand-built invoker:
Pipeline p = TestPipeline.create();
p.apply(Create.of(...)).apply(ParDo.of(myDoFn)); // runner supplies offsets if supported

Type guard

boolean hasRecordOffsets(DoFn<?, ?> fn) {
  return fn instanceof org.apache.beam.sdk.transforms.DoFn.HasKeys // or check SDF signature: element+restriction+tracker params
      || java.util.Arrays.stream(fn.getClass().getDeclaredMethods())
          .anyMatch(m -> m.isAnnotationPresent(DoFn.ProcessElement.class)
              && m.getParameterCount() > 1);
}

Try / catch

try {
  long offset = context.currentRecordOffset();
} catch (UnsupportedOperationException e) {
  // degrade: treat as unknown offset
  processWithoutOffset();
}

Prevention

When it happens

Trigger: A @ProcessElement, @OnTimer, @StartBundle, or @FinishBundle callback (or a callback parameter object like DoFn.OnTimerContext) invokes context.currentRecordOffset() while the invoker was created with UnsupportedInvocationBehavior — typically when the DoFn is invoked outside a real runner pipeline, or the DoFn is not actually a splittable DoFn.

Common situations: Porting code from a splittable DoFn into an ordinary DoFn but keeping currentRecordOffset(); running DoFns in a custom harness or SDK-conformance test using DoFnInvokers.invokerFor(...) without bundle-data support; third-party runner implementations that do not implement record offsets.

Understand the failure class

Background: UnsupportedOperationException and "is not supported" errors: when a library deliberately refuses a call — this error's family across 30 libraries.

Related errors


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