prestodb/presto · error · PrestoException

SHEET_INVALID_HEADER

SHEET_INVALID_HEADER

Error message

Duplicated name %s in Column#%s and Column#%s

What it means

getColumns reads the spreadsheet's header row and lowercases each column name to build column handles. If two header cells yield the same lowercase name, a later duplicate would overwrite the earlier mapping, so the connector detects the collision and throws SHEET_INVALID_HEADER, reporting the duplicated name and the two column labels (A, B, C, ...) where it occurred.

Source

Thrown at presto-lark-sheets/src/main/java/com/facebook/presto/lark/sheets/LarkSheetsMetadata.java:295

    private List<LarkSheetsColumnHandle> getColumns(LarkSheetsTableHandle table)
    {
        List<String> header = api.getHeaderRow(table.getSpreadsheetToken(), table.getSheetId(), table.getColumnCount());

        int numColumns = header.size();
        LinkedHashMap<String, LarkSheetsColumnHandle> columns = new LinkedHashMap<>(numColumns);
        for (int i = 0; i < numColumns; i++) {
            String rawColumnName = header.get(i);
            if (rawColumnName == null) {
                // Columns without name are ignored
                continue;
            }
            String columnName = rawColumnName.toLowerCase(ENGLISH);
            LarkSheetsColumnHandle column = columns.get(columnName);
            if (column != null) {
                String label = LarkSheetsUtil.columnIndexToColumnLabel(i);
                String prevLabel = LarkSheetsUtil.columnIndexToColumnLabel(column.getIndex());
                throw new PrestoException(SHEET_INVALID_HEADER,
                        format("Duplicated name %s in Column#%s and Column#%s", columnName, prevLabel, label));
            }
            columns.put(columnName, new LarkSheetsColumnHandle(columnName, VARCHAR, i));
        }
        return ImmutableList.copyOf(columns.values());
    }

    private static List<SheetInfo> filterSheets(List<SheetInfo> sheets, String filter)
    {
        requireNonNull(sheets, "sheets is null");
        checkArgument(!isNullOrEmpty(filter), "filter is null or empty");

        char firstChar = filter.charAt(0);
        if (firstChar == '$') {
            // filter by index
            Matcher matcher = INDEX_PATTERN.matcher(filter);
            if (matcher.matches()) {
                int i = Integer.parseInt(filter.substring(1));

View on GitHub (pinned to 55bb57d202)

Solutions

  1. Rename one of the duplicate header cells in the Lark sheet so all headers are unique after lowercasing.
  2. Trim whitespace and check case-only differences ('Name' vs 'name') in the header row.
  3. Restructure the sheet so each column has a distinct header name.
  4. If it must stay, split into two sheets and reference the one with clean headers.

Example fix

// header row before: A='Name', B='Age', C='name'
// after
// header row: A='Name', B='Age', C='Nickname'
Defensive patterns

Strategy: validation

Validate before calling

Set<String> seen = new HashSet<>();
for (String header : headerRow) {
    String k = header.trim().toLowerCase(Locale.ROOT);
    if (!seen.add(k)) throw new IllegalStateException("Duplicate header after normalization: " + k);
}

Prevention

When it happens

Trigger: Sheet header row contains two cells whose lowercase forms are equal (e.g. 'Name' in column A and 'name' in column C, or exact duplicates), detected while building column handles for a table.

Common situations: Spreadsheet edited by multiple people who added near-duplicate headers ('Email', 'email'); trailing spaces or case differences that collapse after lowercasing; imported CSV headers with repeated fields.

Related errors


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