{"record":{"id":"16e803341db8be0a","repo":"apache/beam","slug":"recordoffset-unsupported-in-s","errorCode":null,"errorMessage":"RecordOffset unsupported in %s","messagePattern":"RecordOffset unsupported in (.+?)","errorType":"exception","errorClass":"UnsupportedOperationException","httpStatus":null,"severity":"error","filePath":"sdks/java/core/src/main/java/org/apache/beam/sdk/transforms/reflect/DoFnInvoker.java","lineNumber":360,"sourceCode":"      throw new UnsupportedOperationException(\n          String.format(\"Timestamp unsupported in %s\", getErrorContext()));\n    }\n\n    @Override\n    public @Nullable String currentRecordId(DoFn<InputT, OutputT> doFn) {\n      throw new UnsupportedOperationException(\n          String.format(\"RecordId unsupported in %s\", getErrorContext()));\n    }\n\n    @Override\n    public @Nullable Long currentRecordOffset(DoFn<InputT, OutputT> doFn) {\n      throw new UnsupportedOperationException(\n          String.format(\"RecordOffset unsupported in %s\", getErrorContext()));\n    }\n\n    @Override\n    public Instant fireTimestamp(DoFn<InputT, OutputT> doFn) {\n      throw new UnsupportedOperationException(\n          String.format(\"FireTimestamp unsupported in %s\", getErrorContext()));\n    }\n\n    @Override\n    public CausedByDrain causedByDrain(DoFn<InputT, OutputT> doFn) {\n      throw new UnsupportedOperationException(\n          String.format(\"CausedByDrain unsupported in %s\", getErrorContext()));\n    }\n\n    @Override\n    public ValueKind valueKind(DoFn<InputT, OutputT> doFn) {\n      throw new UnsupportedOperationException(\n          String.format(\"ValueKind unsupported in %s\", getErrorContext()));\n    }\n\n    @Override\n    public String timerId(DoFn<InputT, OutputT> doFn) {\n      throw new UnsupportedOperationException(","sourceCodeStart":342,"sourceCodeEnd":378,"githubUrl":"https://github.com/apache/beam/blob/12126d8942aaf848030c478b4c6a28c6af861c66/sdks/java/core/src/main/java/org/apache/beam/sdk/transforms/reflect/DoFnInvoker.java#L342-L378","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Delete the currentRecordOffset() call or replace it with tracking inside your own restriction tracker","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","In tests, use DoFnTestUtils.runFunction or TestPipeline so bundle-data-capable runners drive the DoFn","Catch UnsupportedOperationException around the accessor and degrade gracefully if offsets are optional"],"exampleFix":"// before\nlong offset = context.currentRecordOffset();\n// after\n// track progress via your RestrictionTracker instead\nProgress p = restrictionTracker.getProgress();","handlingStrategy":"try-catch","validationCode":"// Ensure the callback is only invoked by a bundle-data-capable runner path,\n// e.g. run through TestPipeline instead of a hand-built invoker:\nPipeline p = TestPipeline.create();\np.apply(Create.of(...)).apply(ParDo.of(myDoFn)); // runner supplies offsets if supported","typeGuard":"boolean hasRecordOffsets(DoFn<?, ?> fn) {\n  return fn instanceof org.apache.beam.sdk.transforms.DoFn.HasKeys // or check SDF signature: element+restriction+tracker params\n      || java.util.Arrays.stream(fn.getClass().getDeclaredMethods())\n          .anyMatch(m -> m.isAnnotationPresent(DoFn.ProcessElement.class)\n              && m.getParameterCount() > 1);\n}","tryCatchPattern":"try {\n  long offset = context.currentRecordOffset();\n} catch (UnsupportedOperationException e) {\n  // degrade: treat as unknown offset\n  processWithoutOffset();\n}","preventionTips":["Reserve currentRecordOffset() for offset-based splittable DoFns only","Prefer RestrictionTracker.getProgress() for position info instead of raw record offsets","Use runner-backed test utilities (TestPipeline, DoFnTestUtils) rather than the default invoker"],"tags":["java","apache-beam","dofn","splittable-dofn","unsupported-operation"],"backgroundTag":"unsupported-operation","analyzedSha":"12126d8942aaf848030c478b4c6a28c6af861c66","analyzedAt":"2026-09-13T01:50:10.254Z","contentChangedAt":"2026-09-13T01:50:10.254Z","schemaVersion":2},"datasetVersion":"2026-09-14T11:17:12.474Z"}