apache/iceberg · error · UnsupportedOperationException

v4 manifests are not bound to a single partition spec

Error message

v4 manifests are not bound to a single partition spec

What it means

Starting with format version 4, manifests are no longer bound to exactly one partition spec; each manifest entry carries its own spec ID. This adapter exposes a v4 ManifestFile through the older single-spec ManifestReader interface, which has a per-manifest partitionSpecId(). Since no single value exists, the accessor intentionally throws UnsupportedOperationException.

Source

Thrown at core/src/main/java/org/apache/iceberg/TrackedFileAdapters.java:439

    private final TrackedFile file;

    private TrackedManifestFile(TrackedFile file) {
      this.file = file;
    }

    @Override
    public String path() {
      return file.location();
    }

    @Override
    public long length() {
      return file.fileSizeInBytes();
    }

    @Override
    public int partitionSpecId() {
      throw new UnsupportedOperationException(
          "v4 manifests are not bound to a single partition spec");
    }

    @Override
    public ManifestContent content() {
      switch (file.contentType()) {
        case DATA_MANIFEST:
          return ManifestContent.DATA;
        case DELETE_MANIFEST:
          return ManifestContent.DELETES;
        default:
          throw new UnsupportedOperationException(
              "Unsupported content type for manifests: " + file.contentType());
      }
    }

    @Override
    public long sequenceNumber() {

View on GitHub (pinned to 86d9c8fc54)

Solutions

  1. Use the v4-aware API: read per-entry partition spec IDs from manifest entries instead of the manifest-level specId()
  2. Check the table's formatVersion and branch your code: specId() for v<=3, entry-level specs for v4
  3. Upgrade dependent code/tooling to an Iceberg release with v4 manifest support
  4. If a single spec is genuinely required, keep the table at format version <= 3

Example fix

// before
int specId = (int) manifestFile.partitionSpecId();
// after
int specId = TableUtil.formatVersion(table) >= 4
    ? entryFile.partitionSpecId()   // entry-level spec from v4 manifest entries
    : (int) manifestFile.partitionSpecId();
Defensive patterns

Strategy: type-guard

Validate before calling

boolean supportsManifestSpecId(Table table) {
  return TableUtil.formatVersion(table) < 4;
}

Type guard

Long safeSpecId(ManifestFile mf, int formatVersion) {
  return formatVersion >= 4 ? null : (long) mf.partitionSpecId();
}

Try / catch

long specId;
try { specId = mf.partitionSpecId(); }
catch (UnsupportedOperationException e) {
  // v4: read per-entry spec IDs instead
  specId = -1;
}

Prevention

When it happens

Trigger: Calling partitionSpecId() on the ManifestFileAdapter produced for a v4 manifest file (TrackedFileAdapters wrapping a v4 ManifestFile into the legacy ManifestFile interface), e.g. when planning scans or listing manifests and reading manifest.specId() on a v3->v4 table.

Common situations: Code written for v1-v3 tables reading manifest.specId() now running against a v4 table; third-party tools/integrations that assume one spec per manifest; maintenance jobs (rewrite/expire) that inspect partition specs via the legacy adapter.

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/iceberg@86d9c8fc54 (2026-09-12). Data as JSON: /api/errors/27458768e7dc334e. Report an issue: GitHub.