prestodb/presto · error · SemanticException

TABLE_FUNCTION_INVALID_FUNCTION_ARGUMENT

TABLE_FUNCTION_INVALID_FUNCTION_ARGUMENT

Error message

Duplicate argument name: %s

What it means

When analyzing a table function invocation, StatementAnalyzer collects each named argument's canonical name into a Set. If the same argument name appears twice, the set-add fails and this SemanticException (TABLE_FUNCTION_INVALID_FUNCTION_ARGUMENT) is thrown. Table function arguments must be passed by unique names because each name is matched to exactly one ArgumentSpecification.

Source

Thrown at presto-main-base/src/main/java/com/facebook/presto/sql/analyzer/StatementAnalyzer.java:1977

            }
        }

        private ArgumentsAnalysis mapTableFunctionsArgsByName(List<ArgumentSpecification> argumentSpecifications, List<TableFunctionArgument> arguments, Node errorLocation, Optional<Scope> scope)
        {
            ImmutableMap.Builder<String, Argument> passedArguments = ImmutableMap.builder();
            ImmutableList.Builder<TableArgumentAnalysis> tableArgumentAnalyses = ImmutableList.builder();
            Map<String, ArgumentSpecification> argumentSpecificationsByName = new HashMap<>();
            for (ArgumentSpecification argumentSpecification : argumentSpecifications) {
                if (argumentSpecificationsByName.put(argumentSpecification.getName(), argumentSpecification) != null) {
                    // this should never happen, because the argument names are validated at function registration time
                    throw new IllegalStateException("Duplicate argument specification for name: " + argumentSpecification.getName());
                }
            }
            Set<String> uniqueArgumentNames = new HashSet<>();
            for (TableFunctionArgument argument : arguments) {
                String argumentName = argument.getName().orElseThrow(() -> new IllegalStateException("Missing table function argument name")).getCanonicalValue();
                if (!uniqueArgumentNames.add(argumentName)) {
                    throw new SemanticException(TABLE_FUNCTION_INVALID_FUNCTION_ARGUMENT, argument, "Duplicate argument name: %s", argumentName);
                }
                ArgumentSpecification argumentSpecification = argumentSpecificationsByName.remove(argumentName);
                if (argumentSpecification == null) {
                    throw new SemanticException(TABLE_FUNCTION_INVALID_FUNCTION_ARGUMENT, argument, "Unexpected argument name: %s", argumentName);
                }
                ArgumentAnalysis argumentAnalysis = analyzeArgument(argumentSpecification, argument, scope);
                passedArguments.put(argumentSpecification.getName(), argumentAnalysis.getArgument());
                argumentAnalysis.getTableArgumentAnalysis().ifPresent(tableArgumentAnalyses::add);
            }
            // apply defaults for not specified arguments
            for (Map.Entry<String, ArgumentSpecification> entry : argumentSpecificationsByName.entrySet()) {
                ArgumentSpecification argumentSpecification = entry.getValue();
                passedArguments.put(argumentSpecification.getName(), analyzeDefault(argumentSpecification, errorLocation));
            }
            return new ArgumentsAnalysis(passedArguments.buildOrThrow(), tableArgumentAnalyses.build());
        }

        private ArgumentsAnalysis mapTableFunctionArgsByPosition(List<ArgumentSpecification> argumentSpecifications, List<TableFunctionArgument> arguments, Node errorLocation, Optional<Scope> scope)

View on GitHub (pinned to 55bb57d202)

Solutions

  1. Remove the duplicate named argument from the TABLE() invocation, keeping the intended value.
  2. Rename the second argument if it was a typo and actually corresponds to a different parameter of the function.
  3. Check the function's declared argument specifications (SHOW FUNCTIONS / docs) to confirm the correct argument names.

Example fix

// before
SELECT * FROM TABLE(my_fn(key => 'a', key => 'b'));
// after
SELECT * FROM TABLE(my_fn(key => 'a', other => 'b'));
Defensive patterns

Strategy: validation

Validate before calling

// Java: dedupe named table function args before building the invocation
Set<String> seen = new HashSet<>();
for (TableFunctionArgument a : arguments) {
    String name = a.getName().orElseThrow().getCanonicalValue();
    if (!seen.add(name)) {
        throw new IllegalArgumentException("Duplicate table function argument: " + name);
    }
}

Type guard

boolean isUniqueArgs(List<TableFunctionArgument> args) {
    return args.stream()
        .map(a -> a.getName().map(Node::getCanonicalValue).orElse(null))
        .collect(toSet()).size() == args.size();
}

Try / catch

try {
    analyzeTableFunctionCall(node, ...);
} catch (SemanticException e) {
    if (e.getCode() == TABLE_FUNCTION_INVALID_FUNCTION_ARGUMENT) {
        // surface "duplicate argument name" to caller with argument name from e.getErrorMessage()
    }
}

Prevention

When it happens

Trigger: Calling a table function in a FROM clause with the same named argument supplied twice, e.g. `SELECT * FROM TABLE(my_fn(arg => 1, arg => 2))`. Canonical name comparison is case-insensitive via getCanonicalValue(), so `Arg => 1, ARG => 2` also triggers it.

Common situations: Copy-paste of an argument list where one argument wasn't updated; combining generated SQL fragments that both supply the same argument; misunderstanding case-insensitivity of argument names; typo where the second argument was meant to have a different name.

Related errors


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