{"record":{"id":"e6d23091cf9f46e7","repo":"prestodb/presto","slug":"iceberg-invalid-snapshot-id-e6d230","errorCode":"ICEBERG_INVALID_SNAPSHOT_ID","errorMessage":"Invalid snapshot [%s] for table: %s","messagePattern":"Invalid snapshot \\[(.+?)\\] for table: (.+?)","errorType":"error_code","errorClass":"PrestoException","httpStatus":null,"severity":"error","filePath":"presto-iceberg/src/main/java/com/facebook/presto/iceberg/IcebergUtil.java","lineNumber":386,"sourceCode":"                        .map(ManifestFile::partitionSpecId)\n                        .map(specId -> table.specs().get(specId))\n                        .collect(toImmutableSet()))\n                .orElseGet(() -> ImmutableSet.copyOf(table.specs().values()));   // No snapshot, so no data. This case doesn't matter.\n\n        return table.spec().fields().stream()\n                .filter(field -> field.transform().isIdentity() &&\n                        partitionSpecs.stream()\n                                .allMatch(partitionSpec -> partitionSpec.getFieldsBySourceId(field.sourceId()).stream()\n                                        .anyMatch(partitionField -> partitionField.transform().isIdentity())))\n                .map(field -> IcebergColumnHandle.create(table.schema().findField(field.sourceId()), typeManager, PARTITION_KEY))\n                .collect(toImmutableList());\n    }\n\n    public static Optional<Long> resolveSnapshotIdByName(Table table, IcebergTableName name)\n    {\n        if (name.getSnapshotId().isPresent()) {\n            if (table.snapshot(name.getSnapshotId().get()) == null) {\n                throw new PrestoException(ICEBERG_INVALID_SNAPSHOT_ID, format(\"Invalid snapshot [%s] for table: %s\", name.getSnapshotId().get(), table));\n            }\n            return name.getSnapshotId();\n        }\n\n        if (name.getBranchName().isPresent()) {\n            String branchName = name.getBranchName().get();\n            SnapshotRef branchRef = table.refs().get(branchName);\n            if (branchRef != null && branchRef.isBranch()) {\n                return Optional.of(branchRef.snapshotId());\n            }\n            throw new PrestoException(NOT_FOUND, format(\"Branch '%s' does not exist in table %S\", branchName, table));\n        }\n\n        if (name.getTableType() == IcebergTableType.CHANGELOG) {\n            return Optional.ofNullable(SnapshotUtil.oldestAncestor(table)).map(Snapshot::snapshotId);\n        }\n\n        return tryGetCurrentSnapshot(table).map(Snapshot::snapshotId);","sourceCodeStart":368,"sourceCodeEnd":404,"githubUrl":"https://github.com/prestodb/presto/blob/55bb57d202de3b926896fa966c2c4a44c779634e/presto-iceberg/src/main/java/com/facebook/presto/iceberg/IcebergUtil.java#L368-L404","documentation":"This error is thrown when a query explicitly references an Iceberg snapshot by ID (e.g. 'table@snapshotId' or FOR VERSION AS OF), but the given snapshot ID does not exist in the table's metadata. IcebergUtil.resolveSnapshotIdByName looks the ID up in the loaded Table's snapshot list and throws ICEBERG_INVALID_SNAPSHOT_ID if it is absent. It protects queries from silently resolving to the wrong version of the table.","triggerScenarios":"Calling resolveSnapshotIdByName with an IcebergTableName whose snapshotId is present but is not a known snapshot of the table — e.g. a hard-coded or stale snapshot ID from a previous metadata version, an ID from a different table, or one expired by snapshot expiration (expire_snapshots).","commonSituations":"Re-running a saved query with FOR VERSION AS OF after snapshots were expired by retention jobs; copying a snapshot ID from a dev table to a prod table; a long-running pipeline caching snapshot IDs that were garbage-collected by Iceberg's snapshot expiration.","solutions":["Re-list the table's valid snapshots (SELECT snapshot_id FROM \"table$snapshots\") and use an existing snapshot ID","If the old snapshot was expired, query the current table state without the @snapshotId / FOR VERSION AS OF clause","Correct the snapshot ID if it was copied from another table or mistyped","Increase snapshot expiration retention (expire_snapshots older_than) if historical snapshots must remain queryable"],"exampleFix":"// before\nSELECT * FROM catalog.db.t FOR VERSION AS OF 8745035778589976427; -- expired\n// after\nSELECT snapshot_id FROM \"catalog.db.t$snapshots\"; -- pick a valid ID\nSELECT * FROM catalog.db.t FOR VERSION AS OF 9126358462717461923;","handlingStrategy":"validation","validationCode":"// Before issuing FOR VERSION AS OF, verify the snapshot exists\nList<Long> valid = /* SELECT snapshot_id FROM \"db.t$snapshots\" */;\nif (!valid.contains(requestedSnapshotId)) {\n    throw new IllegalArgumentException(\"Snapshot \" + requestedSnapshotId + \" not in t$snapshots\");\n}","typeGuard":"boolean snapshotExists(Table table, long id) {\n    return table.snapshot(id) != null;\n}","tryCatchPattern":"try {\n    return resolveSnapshotIdByName(table, name);\n} catch (PrestoException e) {\n    if (e.getErrorCode().getCode() == ICEBERG_INVALID_SNAPSHOT_ID.getCode()) {\n        // fall back to current snapshot\n        return Optional.of(table.currentSnapshot().snapshotId());\n    }\n    throw e;\n}","preventionTips":["Resolve snapshot IDs from \"table$snapshots\" at runtime instead of hard-coding them","Account for expire_snapshots retention: old IDs become invalid over time","Never reuse snapshot IDs across different tables","Pin historical query results by copying data, not by retaining fragile snapshot references"],"tags":["iceberg","snapshot","time-travel","presto"],"backgroundTag":"invalid-snapshot-id","analyzedSha":"55bb57d202de3b926896fa966c2c4a44c779634e","analyzedAt":"2026-09-04T12:50:26.162Z","contentChangedAt":"2026-09-04T12:50:26.162Z","schemaVersion":2},"datasetVersion":"2026-09-11T21:17:09.523Z"}