prestodb/presto · error · IllegalArgumentException

Expected field to be

Error message

Expected field to be %s, actual %s (field %s)

What it means

GenericHiveRecordCursor.validateType asserts that the Java type requested from a cursor column (e.g. long.class, boolean.class) matches the column's declared type in the Hive schema. Presto's page-building machinery calls getLong/getBoolean/getDouble/getSlice/getObject with the expected type per column; a mismatch means the connector's column-to-type mapping is inconsistent with what the reader actually produces. The raw IllegalArgumentException (not a PrestoException) is deliberate to avoid boxing fieldId in the hot read loop.

Solutions

  1. Check that each HiveColumnHandle's getColumnType().getJavaType() matches the value type your cursor produces for that field index.
  2. Verify column ordering: the types array passed to GenericHiveRecordCursor must align positionally with the columns list.
  3. If a custom RecordCursorProvider is in use, fix it to construct column handles with types matching the underlying record reader's object inspectors.
  4. If it appeared after a Presto/Hive connector upgrade, review TypeManager/TypeRegistry mapping changes for the affected Hive type.

Example fix

// before
columns.add(new HiveColumnHandle("extra", 1, HIVE_INT, INTEGER, Optional.empty()));
// after (type must match what validateType expects per get call)
columns.add(new HiveColumnHandle("extra", 1, HIVE_INT, BIGINT, Optional.empty())); // if cursor.getLong() is called
Defensive patterns

Strategy: type-guard

Validate before calling

// validate column handle types before building the cursor
for (int i = 0; i < columns.size(); i++) {
    if (!types[i].getJavaType().equals(expectedJavaTypes.get(i))) {
        throw new IllegalStateException("Column " + i + " type mismatch: " + types[i]);
    }
}

Type guard

boolean supportsRead(Class<?> actual, Class<?> requested) {
    return actual != null && actual.equals(requested);
}

Try / catch

try {
    long v = cursor.getLong(field);
} catch (IllegalArgumentException e) {
    if (e.getMessage().startsWith("Expected field to be")) {
        throw new IllegalStateException("Column handle type mapping is wrong: " + e.getMessage(), e);
    }
    throw e;
}

Prevention

When it happens

Trigger: Calling cursor.getLong(field) (or getBoolean/getDouble/getSlice/getObject) on a field whose declared HiveColumnHandle type does not equal types[fieldId].getJavaType() — typically when a custom column handle or type mapping returns the wrong JavaType for that field position.

Common situations: Custom or third-party Hive readers (e.g. custom record cursor providers, ORC/Parquet reader plugins, query engines embedding the Hive connector) that build RecordCursor columns with mismatched type bindings; type registry/mapping changes after a Presto version upgrade; a reader provider that reorders columns without reordering types.

Related errors


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

Appendix: source

Thrown at presto-hive/src/main/java/com/facebook/presto/hive/GenericHiveRecordCursor.java:581

        else if (DATE.equals(type)) {
            parseLongColumn(column);
        }
        else if (TIMESTAMP.equals(type)) {
            parseLongColumn(column);
        }
        else if (type instanceof DecimalType) {
            parseDecimalColumn(column);
        }
        else {
            throw new UnsupportedOperationException("Unsupported column type: " + type);
        }
    }

    private void validateType(int fieldId, Class<?> type)
    {
        if (!types[fieldId].getJavaType().equals(type)) {
            // we don't use Preconditions.checkArgument because it requires boxing fieldId, which affects inner loop performance
            throw new IllegalArgumentException(String.format("Expected field to be %s, actual %s (field %s)", type, types[fieldId], fieldId));
        }
    }

    @Override
    public void close()
    {
        // some hive input formats are broken and bad things can happen if you close them multiple times
        if (closed) {
            return;
        }
        closed = true;

        updateCompletedBytes();

        try {
            recordReader.close();
        }
        catch (IOException e) {

View on GitHub (pinned to 55bb57d202)