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
- Use the v4-aware API: read per-entry partition spec IDs from manifest entries instead of the manifest-level specId()
- Check the table's formatVersion and branch your code: specId() for v<=3, entry-level specs for v4
- Upgrade dependent code/tooling to an Iceberg release with v4 manifest support
- 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
- Gate manifest.specId() usage on formatVersion < 4
- Use per-entry partition spec APIs for v4 tables
- Keep integrations/tools updated before upgrading tables to v4
- Write a compatibility layer instead of assuming one spec per manifest
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
- Cannot drop partition field in non-Iceberg table: $table
- %s does not implement deleteFile
- %s does not implement addFile
- %s does not implement dataSequenceNumber
- %s does not implement removeRows
AI-assisted analysis of apache/iceberg@86d9c8fc54 (2026-09-12).
Data as JSON: /api/errors/27458768e7dc334e.
Report an issue: GitHub.