Skip to content

Partial-progress compaction can lose manifest-list encryption keys聽#17989

Closed

Description

@smaheshwar-pltr

Apache Iceberg version

1.11.0 (latest release)

Query engine

Spark

Please describe the bug 馃悶

Concurrent partial-progress commits on an encrypted Hive table can commit a snapshot without the wrapped key for its manifest list. The rewrite reports failed groups and returns, but the snapshot's key is lost, leaving the table unreadable to users and seemingly lost. They see:

java.util.concurrent.ExecutionException: java.lang.NullPointerException:
Cannot invoke "org.apache.iceberg.encryption.EncryptedKey.encryptedKeyMetadata()"
because "manifestListKey" is null

Caused by: java.lang.NullPointerException
  at org.apache.iceberg.encryption.EncryptionUtil.decryptManifestListKeyMetadata(...)
  at org.apache.iceberg.BaseSnapshot.allManifests(...)
  at org.apache.iceberg.FastAppend.apply(...)
  at org.apache.iceberg.SnapshotProducer.commit(...)

Willingness to contribute

  • I can contribute a fix for this bug independently
  • I would be willing to contribute a fix for this bug with guidance from the Iceberg community
  • I cannot contribute a fix for this bug at this time

Activity

  1. smaheshwar-pltr commented on Sep 7, 2026

    @smaheshwar-pltr
    ContributorAuthor

    Example reproduction below. It's a bit heavy; there may have been an easier repro here but couldn't find one easily.

    @TestTemplate
    public void testConcurrentCompactionPreservesEncryptionKeys() {
        // Partitioned encrypted table: one file per partition => many file groups for rewrite
        sql("DROP TABLE %s", tableName);
        sql(
            "CREATE TABLE %s (id bigint, data string) USING iceberg PARTITIONED BY (id) "
                + "TBLPROPERTIES ('encryption.key-id'='%s', 'format-version'='3')",
            tableName, UnitestKMS.MASTER_KEY_NAME1);
        sql("INSERT INTO %s SELECT id, 'v' FROM (SELECT explode(sequence(0, 511)) AS id)", tableName);
    
        validationCatalog.initialize(catalogName, catalogConfig);
        Table table = validationCatalog.loadTable(tableIdent);
    
        SparkActions.get(spark)
            .rewriteDataFiles(table)
            .binPack()
            .option("partial-progress.enabled", "true")
            .option("partial-progress.max-commits", "2000")
            .option("max-concurrent-file-group-rewrites", "128")
            .option("rewrite-all", "true")
            .execute();
    
        assertThat(currentDataFiles(validationCatalog.loadTable(tableIdent))).isNotEmpty();
      }
  2. smaheshwar-pltr commented on Sep 7, 2026

    @smaheshwar-pltr
    ContributorAuthor

    A couple notes: (went down a few routes investigating this, so wanted to track)

  3. smaheshwar-pltr commented on Sep 24, 2026

    @smaheshwar-pltr
    ContributorAuthor

    Fixed by #17984

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions