prestodb/presto · error · PrestoException

NOT_FOUND

NOT_FOUND

Error message

Branch '%s' does not exist in table %S

What it means

Thrown by resolveSnapshotIdByName when a table name references a branch (e.g. 'table@branch' syntax) that is not an existing branch ref in the Iceberg table metadata. The code checks table.refs() for the name and whether it is actually a branch; if not found or not a branch, it throws NOT_FOUND. This ensures branch-based reads/writes target a real Iceberg branch.

Source

Thrown at presto-iceberg/src/main/java/com/facebook/presto/iceberg/IcebergUtil.java:397

                .collect(toImmutableList());
    }

    public static Optional<Long> resolveSnapshotIdByName(Table table, IcebergTableName name)
    {
        if (name.getSnapshotId().isPresent()) {
            if (table.snapshot(name.getSnapshotId().get()) == null) {
                throw new PrestoException(ICEBERG_INVALID_SNAPSHOT_ID, format("Invalid snapshot [%s] for table: %s", name.getSnapshotId().get(), table));
            }
            return name.getSnapshotId();
        }

        if (name.getBranchName().isPresent()) {
            String branchName = name.getBranchName().get();
            SnapshotRef branchRef = table.refs().get(branchName);
            if (branchRef != null && branchRef.isBranch()) {
                return Optional.of(branchRef.snapshotId());
            }
            throw new PrestoException(NOT_FOUND, format("Branch '%s' does not exist in table %S", branchName, table));
        }

        if (name.getTableType() == IcebergTableType.CHANGELOG) {
            return Optional.ofNullable(SnapshotUtil.oldestAncestor(table)).map(Snapshot::snapshotId);
        }

        return tryGetCurrentSnapshot(table).map(Snapshot::snapshotId);
    }

    public static long getSnapshotIdTimeOperator(Table table, long millisUtc, VersionOperator operator)
    {
        return table.history().stream()
                .filter(logEntry -> operator == VersionOperator.EQUAL ? logEntry.timestampMillis() <= millisUtc : logEntry.timestampMillis() < millisUtc)
                .max(comparing(HistoryEntry::timestampMillis))
                .orElseThrow(() -> new PrestoException(ICEBERG_INVALID_TABLE_TIMESTAMP, format("No history found based on timestamp for table %s", table.name())))
                .snapshotId();
    }

View on GitHub (pinned to 55bb57d202)

Solutions

  1. Verify the branch exists: SELECT name FROM "catalog.db.t$refs" WHERE type = 'BRANCH'
  2. Create the missing branch with ALTER TABLE ... CREATE BRANCH (or ALTER TABLE ... CREATE BRANCH AS OF the desired snapshot)
  3. Fix the branch-name typo or case in the query
  4. If the ref is a tag, use tag syntax (FOR VERSION AS OF tag) instead of branch syntax

Example fix

// before
SELECT * FROM catalog.db.t@audit_branch; -- branch never created
// after
ALTER TABLE catalog.db.t CREATE BRANCH audit_branch;
SELECT * FROM catalog.db.t@audit_branch;
Defensive patterns

Strategy: validation

Validate before calling

// Check refs before using branch syntax
boolean ok = table.refs().containsKey(branchName)
          && table.refs().get(branchName).isBranch();
if (!ok) throw new IllegalArgumentException("Not a branch: " + branchName);

Type guard

boolean isBranch(Table table, String name) {
    SnapshotRef ref = table.refs().get(name);
    return ref != null && ref.isBranch();
}

Try / catch

try {
    return resolveSnapshotIdByName(table, name);
} catch (PrestoException e) {
    if (e.getErrorCode() == StandardErrorCode.NOT_FOUND) {
        throw new UserError("Branch not found; valid branches: " + table.refs().keySet());
    }
    throw e;
}

Prevention

When it happens

Trigger: Calling resolveSnapshotIdByName with an IcebergTableName carrying a branchName that either does not exist in table.refs(), or exists as a non-branch ref (e.g. a tag). Note the format string uses %S so the branch name is printed uppercase in the message.

Common situations: Typo in the branch name in the SQL (table@maim instead of @main); referencing a branch that was deleted or never created (CREATE BRANCH not run); accidentally using a tag name where a branch is expected; case-sensitivity mismatch between SQL and the created branch name.

Understand the failure class

Background: NOT_FOUND error code: why tRPC, Harbor, Nacos and other libraries return 404 "not found" errors for resources that may still exist — this error's family across 11 libraries.

Related errors


AI-assisted analysis of prestodb/presto@55bb57d202 (2026-09-04). Data as JSON: /api/errors/e6ff46c2e6ff12f9. Report an issue: GitHub.