{"record":{"id":"f898a1d2d558e730","repo":"apache/beam","slug":"getcurrentrecordid-must-be-overridden-if-requiresdeduping","errorCode":null,"errorMessage":"getCurrentRecordId() must be overridden if requiresDeduping returns true()","messagePattern":"getCurrentRecordId\\(\\) must be overridden if requiresDeduping returns true\\(\\)","errorType":"exception","errorClass":"IllegalStateException","httpStatus":null,"severity":"error","filePath":"sdks/java/core/src/main/java/org/apache/beam/sdk/io/UnboundedSource.java","lineNumber":222,"sourceCode":"     * of the same logical record read from the underlying data source.\n     *\n     * <p>It is only necessary to override this if {@link #requiresDeduping} has been overridden to\n     * return true.\n     *\n     * <p>For example, this could be a hash of the record contents, or a logical ID present in the\n     * record. If this is generated as a hash of the record contents, it should be at least 16 bytes\n     * (128 bits) to avoid collisions.\n     *\n     * <p>This method has the same restrictions on when it can be called as {@link #getCurrent} and\n     * {@link #getCurrentTimestamp}.\n     *\n     * @throws NoSuchElementException if the reader is at the beginning of the input and {@link\n     *     #start} or {@link #advance} wasn't called, or if the last {@link #start} or {@link\n     *     #advance} returned {@code false}.\n     */\n    public byte[] getCurrentRecordId() throws NoSuchElementException {\n      if (getCurrentSource().requiresDeduping()) {\n        throw new IllegalStateException(\n            \"getCurrentRecordId() must be overridden if requiresDeduping returns true()\");\n      }\n      return EMPTY;\n    }\n\n    /* Returns the offset for the current record of this unbounded reader. */\n    public byte[] getCurrentRecordOffset() {\n      throw new RuntimeException(\n          \"UnboundedReader must override getCurrentRecordOffset() if offset-based deduplication is enabled for the UnboundedSource.\");\n    }\n\n    /**\n     * Returns a timestamp before or at the timestamps of all future elements read by this reader.\n     *\n     * <p>This can be approximate. If records are read that violate this guarantee, they will be\n     * considered late, which will affect how they will be processed. See {@link\n     * org.apache.beam.sdk.transforms.windowing.Window} for more information on late data and how to\n     * handle it.","sourceCodeStart":204,"sourceCodeEnd":240,"githubUrl":"https://github.com/apache/beam/blob/12126d8942aaf848030c478b4c6a28c6af861c66/sdks/java/core/src/main/java/org/apache/beam/sdk/io/UnboundedSource.java#L204-L240","documentation":"UnboundedReader.getCurrentRecordId() returns a stable unique id for the current record so runners can deduplicate records from unbounded sources. The default implementation throws IllegalStateException if the source declares requiresDeduping() == true, because a deduping source must supply real record ids; otherwise it returns an empty byte array.","triggerScenarios":"Calling getCurrentRecordId() on a reader whose UnboundedSource.requiresDeduping() returns true while the reader does not override getCurrentRecordId() — typically invoked by the runner (processElement/recordId paths) when offset- or id-based dedup is enabled.","commonSituations":"Writing a custom UnboundedReader for a dedup-required source (e.g. queues with possible redeliveries) and forgetting to implement record ids; upgrading Beam so a runner now asks for record ids on sources that previously returned true from requiresDeduping() without consequence.","solutions":["Override getCurrentRecordId() in your UnboundedReader and return a stable unique id per record (e.g. offset or message id bytes).","If the source cannot produce stable ids, change requiresDeduping() to return false and handle duplicates downstream.","Check that the reader, not just the source, implements the dedup API — the check lives in UnboundedReader."],"exampleFix":"// before\nclass MyReader extends UnboundedReader<MyRecord> {\n  // no getCurrentRecordId override\n}\n\n// after\nclass MyReader extends UnboundedReader<MyRecord> {\n  @Override\n  public byte[] getCurrentRecordId() {\n    return Longs.toByteArray(currentRecord.getOffset());\n  }\n}","handlingStrategy":"validation","validationCode":"if (source.requiresDeduping()) {\n  checkState(!(reader.getClass().getMethod(\"getCurrentRecordId\").getDeclaringClass() == UnboundedReader.class), \"reader must override getCurrentRecordId()\");\n}","typeGuard":null,"tryCatchPattern":"try {\n  byte[] id = reader.getCurrentRecordId();\n} catch (IllegalStateException e) {\n  // reader does not support dedup ids: fall back to non-dedup processing\n}","preventionTips":["Implement getCurrentRecordId in every custom UnboundedReader whose source requires deduping.","Keep requiresDeduping() false if you cannot produce stable record ids.","Test readers on runners with dedup enabled before production."],"tags":["java","apache-beam","io","unbounded-source","dedup"],"backgroundTag":"method-not-implemented","analyzedSha":"12126d8942aaf848030c478b4c6a28c6af861c66","analyzedAt":"2026-09-13T01:50:10.254Z","contentChangedAt":"2026-09-13T01:50:10.254Z","schemaVersion":2},"datasetVersion":"2026-09-14T16:17:12.679Z"}