apache/iceberg · error · IllegalArgumentException

Cannot find unpartitioned spec in specs:

Error message

Cannot find unpartitioned spec in specs: 

What it means

TrackedFileAdapters needs the spec ID of the unpartitioned partition spec within a table's specs map. If none of the specs in specsById is unpartitioned (i.e., the table was always partitioned), the lookup helper throws IllegalArgumentException listing the spec IDs it searched.

Source

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

    }
  }

  private static int resolveSpecId(TrackedFile file, Map<Integer, PartitionSpec> specsById) {
    Integer specId = file.specId();
    if (specId != null) {
      Preconditions.checkArgument(
          specsById.containsKey(specId), "Cannot find partition spec for spec ID: %s", specId);
      return specId;
    }

    // A null spec ID means the file is unpartitioned; use the table's unpartitioned spec.
    for (PartitionSpec spec : specsById.values()) {
      if (spec.isUnpartitioned()) {
        return spec.specId();
      }
    }

    throw new IllegalArgumentException(
        "Cannot find unpartitioned spec in specs: " + specsById.keySet());
  }
}

View on GitHub (pinned to 86d9c8fc54)

Solutions

  1. Check that the table actually has an unpartitioned spec (specs.containsValue matching isUnpartitioned) before invoking the adapter path.
  2. Pass the correct target spec ID explicitly instead of relying on the unpartitioned-spec lookup.
  3. Guard the call: iterate specsById yourself and fall back when no unpartitioned spec exists.

Example fix

// before
long specId = table.specs().values().stream().filter(PartitionSpec::isUnpartitioned)...; // may throw

// after
PartitionSpec unpartitioned = table.specs().values().stream()
    .filter(PartitionSpec::isUnpartitioned).findFirst().orElse(null);
if (unpartitioned == null) { /* handle fully-partitioned table */ }
Defensive patterns

Strategy: validation

Validate before calling

boolean hasUnpartitioned = table.specs().values().stream().anyMatch(PartitionSpec::isUnpartitioned);
if (!hasUnpartitioned) { /* take a partitioned-spec path */ }

Try / catch

try { specId = findUnpartitionedSpecId(specs); } catch (IllegalArgumentException e) { specId = fallbackSpecId(specs); }

Prevention

When it happens

Trigger: An adapter path requires an unpartitioned spec (e.g., to interpret a file written before partitioning or to map an empty partition key) but the table's metadata contains only partitioned specs.

Common situations: Tracking files on a table that was partitioned from creation, with code that assumes an unpartitioned spec (spec 0) always exists; metadata rewritten so spec 0 is partitioned.

Understand the failure class

Background: 'Could not be found', 'does not exist', 'not found in database': the resource-not-found family when an ID, slug, key, or URI lookup comes back empty — this error's family across 20 libraries.

Related errors


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