prestodb/presto · error · PrestoException

INVALID_FUNCTION_ARGUMENT

INVALID_FUNCTION_ARGUMENT

Error message

the size of fromType and toType must match

What it means

RowToRowCast specializes the ROW(F...) -> ROW(T...) cast. The generated row cast requires source and target rows to have the same number of fields; when the type variables have differing field counts it throws INVALID_FUNCTION_ARGUMENT, since a cast between different-arity rows cannot be generated.

Source

Thrown at presto-main-base/src/main/java/com/facebook/presto/operator/scalar/RowToRowCast.java:86

public class RowToRowCast
        extends SqlOperator
{
    public static final RowToRowCast ROW_TO_ROW_CAST = new RowToRowCast();

    private RowToRowCast()
    {
        super(CAST, ImmutableList.of(withVariadicBound("F", "row"), withVariadicBound("T", "row")), ImmutableList.of(), parseTypeSignature("T"), ImmutableList.of(parseTypeSignature("F")));
    }

    @Override
    public BuiltInScalarFunctionImplementation specialize(BoundVariables boundVariables, int arity, FunctionAndTypeManager functionAndTypeManager)
    {
        checkArgument(arity == 1, "Expected arity to be 1");
        Type fromType = boundVariables.getTypeVariable("F");
        Type toType = boundVariables.getTypeVariable("T");
        if (fromType.getTypeParameters().size() != toType.getTypeParameters().size()) {
            throw new PrestoException(StandardErrorCode.INVALID_FUNCTION_ARGUMENT, "the size of fromType and toType must match");
        }
        Class<?> castOperatorClass = generateRowCast(fromType, toType, functionAndTypeManager);
        MethodHandle methodHandle = methodHandle(castOperatorClass, "castRow", SqlFunctionProperties.class, Block.class);
        return new BuiltInScalarFunctionImplementation(
                false,
                ImmutableList.of(valueTypeArgumentProperty(RETURN_NULL_ON_NULL)),
                methodHandle);
    }

    private static Class<?> generateRowCast(Type fromType, Type toType, FunctionAndTypeManager functionAndTypeManager)
    {
        List<Type> toTypes = toType.getTypeParameters();
        List<Type> fromTypes = fromType.getTypeParameters();

        CallSiteBinder binder = new CallSiteBinder();

        // Embed the SHA256 hash code of input and output types into the generated class name instead of the raw type names,
        // which could prevent the class name from hitting the length limitation and invalid characters.

View on GitHub (pinned to 55bb57d202)

Solutions

  1. Make the CAST target row type have the same number of fields as the source (add or remove fields to match).
  2. Rebuild the row explicitly: use row field access (r[1], r[2], ...) and construct a new row of the desired arity.
  3. Fix upstream schema so row arity is stable, or version the row type.
  4. Use try_cast to get NULL instead of failing while migrating.

Example fix

-- before
SELECT CAST(r AS ROW(a INTEGER, b INTEGER)); -- r is ROW(x,y,z)
-- after
SELECT CAST(ROW(r[1], r[2]) AS ROW(a INTEGER, b INTEGER));
Defensive patterns

Strategy: type-guard

Validate before calling

WHERE cardinality(typehints(fromType)) = cardinality(typehints(toType)) -- ensure same field count before CAST

Try / catch

SELECT try_cast(r AS ROW(a INTEGER, b INTEGER)) AS r2;

When it happens

Trigger: Executing CAST(row_value AS ROW(a, b)) where row_value's row type has a different number of fields than the target (e.g. casting ROW(1,2,3) to ROW(x INT, y INT)).

Common situations: Schema drift: an upstream table's row column gained/lost a field while downstream casts still assume the old arity; hand-written casts omitting a field.

Related errors


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