apache/iceberg · error · UnsupportedOperationException

Unsupported metadata stats field ID: ${statId}

Error message

Unsupported metadata stats field ID: ${statId}

What it means

Stats files encode metadata column statistics using synthetic (reserved) field IDs that must fall into known ranges (last-updated-sequence-number, row-id, and content-stats ranges). toFieldId translates a stat ID back to a real field ID; an ID outside every known range cannot be mapped, so the parser refuses it. This protects forward compatibility: unknown reserved IDs mean the stats were written by a newer spec version the reader doesn't understand.

Source

Thrown at core/src/main/java/org/apache/iceberg/StatsUtil.java:110

  /**
   * Return the field ID corresponding to the stats field ID.
   *
   * @param statId the field ID of a field stats struct or field within a stats struct
   * @return ID of the corresponding table field
   * @throws IllegalArgumentException if the stats ID is not valid
   * @throws UnsupportedOperationException if the stats ID is for an unsupported metadata field
   */
  static int toFieldId(int statId) {
    Preconditions.checkArgument(isValidStatId(statId), "Invalid stats field ID: %s", statId);

    if (statId < CONTENT_STATS_RANGE_START) {
      if (inBaseIdRange(LAST_UPDATED_SEQ_NUM_BASE_ID, statId)) {
        return MetadataColumns.LAST_UPDATED_SEQUENCE_NUMBER.fieldId();
      } else if (inBaseIdRange(ROW_ID_BASE_ID, statId)) {
        return MetadataColumns.ROW_ID.fieldId();
      } else {
        throw new UnsupportedOperationException("Unsupported metadata stats field ID: " + statId);
      }
    }

    return (statId - CONTENT_STATS_RANGE_START) / NUM_RESERVED_FIELD_STATS_IDS;
  }

  /**
   * Return the stats offset of a stats field ID.
   *
   * @param statId the field ID of a field stats struct or field within a stats struct
   * @return offset that identifies the stored metric, or 0 for a stats struct's ID
   * @throws IllegalArgumentException if the stats ID is not valid
   * @throws UnsupportedOperationException if the stats ID is for an unsupported metadata field
   */
  static int statOffset(int statId) {
    Preconditions.checkArgument(isValidStatId(statId), "Invalid stats field ID: %s", statId);

    return statId % NUM_RESERVED_FIELD_STATS_IDS;

View on GitHub (pinned to 86d9c8fc54)

Solutions

  1. Upgrade Iceberg to a version that knows the reserved stat ID range used by the stats file
  2. Regenerate the stats file with a compatible writer so it only uses recognized stat IDs
  3. Verify the stats file matches the table format version and spec you are reading with
  4. Skip/ignore the unrecognized stats entries instead of failing plan if your use case tolerates missing stats
Defensive patterns

Strategy: try-catch

Validate before calling

boolean knownReservedId(int statId) {
  return statId >= CONTENT_STATS_RANGE_START
      || inRange(LAST_UPDATED_SEQ_NUM_BASE_ID, statId)
      || inRange(ROW_ID_BASE_ID, statId);
}

Try / catch

try { long fieldId = StatsUtil.toFieldId(statId); }
catch (UnsupportedOperationException e) {
  LOG.warn("Unknown reserved stats ID {} from a newer writer; skipping", statId);
}

Prevention

When it happens

Trigger: Reading a stats (puffin/dv/placeholder-statistics) file whose metadata field statId is below CONTENT_STATS_RANGE_START but not within the LAST_UPDATED_SEQ_NUM or ROW_ID reserved ranges — i.e. StatsUtil.toFieldId(statId) called with an unrecognized reserved ID.

Common situations: Stats files written by a newer Iceberg version with additional reserved metadata stats IDs read by an older reader; hand-edited or corrupted stats files; custom tooling that invents stat IDs instead of using the spec-assigned ranges.

Related errors


AI-assisted analysis of apache/iceberg@86d9c8fc54 (2026-09-12). Data as JSON: /api/errors/6d51e1bc10ce897c. Report an issue: GitHub.