prestodb/presto · error · IllegalArgumentException

Original function call and substitute must both be specified

Error message

Original function call and substitute must both be specified, %s.

What it means

The Verifier's function-call substitution config (function-call-substitutes) expects each substitute entry to be a spec of exactly two parts: the original function call and the replacement expression, separated by '/'. When an entry does not split into exactly two slash-delimited parts, validateAndConstructFunctionCallSubstituteMap throws this IllegalArgumentException. It is a fail-fast config validation error.

Source

Thrown at presto-verifier/src/main/java/com/facebook/presto/verifier/rewrite/FunctionCallRewriter.java:98

            return Optional.empty();
        }
        checkState(typeManager instanceof FunctionAndTypeManager, "FunctionAndTypeManager is required for FunctionCallRewriter.");
        return Optional.of(new FunctionCallRewriter(functionCallSubstitutes, (FunctionAndTypeManager) typeManager));
    }

    public static Multimap<String, FunctionCallSubstitute> validateAndConstructFunctionCallSubstituteMap(String functionCallSubstitutes)
    {
        ImmutableMultimap.Builder<String, FunctionCallSubstitute> map = ImmutableMultimap.builder();
        if (functionCallSubstitutes == null) {
            return map.build();
        }

        Splitter commaSplitter = Splitter.on("/,/").omitEmptyStrings().trimResults();
        Splitter slashSplitter = Splitter.on('/').omitEmptyStrings().trimResults();
        for (String substitute : commaSplitter.split(functionCallSubstitutes)) {
            List<String> specs = slashSplitter.splitToList(substitute);
            if (specs.size() != 2) {
                throw new IllegalArgumentException(String.format("Original function call and substitute must both be specified, %s.", substitute));
            }
            Expression originalExpression = parseOriginalFunctionCall(specs.get(0));
            Expression substituteExpression = parseSubstituteExpression(specs.get(1));

            if (originalExpression instanceof FunctionCall) {
                FunctionCall originalFunction = (FunctionCall) originalExpression;
                map.put(originalFunction.getName().getSuffix(), new FunctionCallSubstitute(originalExpression, substituteExpression));
            }
            else if (originalExpression instanceof CurrentTime) {
                CurrentTime originalFunction = (CurrentTime) originalExpression;
                map.put(originalFunction.getFunction().getName(), new FunctionCallSubstitute(originalExpression, substituteExpression));
            }
        }

        return map.build();
    }

    public RewriterResult rewrite(Statement root)

View on GitHub (pinned to 55bb57d202)

Solutions

  1. Fix the config entry so it has exactly one '/' separating original and substitute, e.g. 'original_fn(x)/replacement_fn(x)'
  2. Escape or remove any literal '/' characters appearing inside function arguments in the spec
  3. Split complex entries: each comma-separated element must contain exactly one pair; move extra mappings to their own entry

Example fix

// before (config value)
functionCallSubstitutes = myfunc(x)
// after (config value)
functionCallSubstitutes = myfunc(x)/myfunc_new(x)
Defensive patterns

Strategy: validation

Validate before calling

// Validate each comma-separated entry has exactly one '/' before configuring
GuavaSplitter.on(",").omitEmptyStrings().trimResults().split(configValue).forEach(entry -> {
    long slashes = entry.chars().filter(c -> c == '/').count();
    if (slashes != 1) {
        throw new IllegalArgumentException("Entry must be 'original/substitute': " + entry);
    }
});

Type guard

boolean isValidSubstituteSpec(String entry) {
    return entry != null && entry.indexOf('/') == entry.lastIndexOf('/') && entry.indexOf('/') >= 0;
}

Try / catch

try {
    rewriter = FunctionCallRewriter.getInstance(functionCallSubstitutes, typeManager);
} catch (IllegalArgumentException e) {
    LOG.error("Bad function-call-substitutes config, skipping rewrites: %s", e.getMessage());
    rewriter = Optional.empty();
}

Prevention

When it happens

Trigger: Passing a functionCallSubstitutes config string where any comma-separated entry contains zero or more than one '/' separator, e.g. 'myfunc(x)' (no substitute) or 'myfunc(x)/abs(x)/extra' (three parts). Note empty segments are omitted, so a double slash yields one part and also fails.

Common situations: Typos in the verifier config properties file; forgetting the second half of a substitute; using a literal '/' inside a function argument without realizing it is the spec separator; copy-pasting entries that contain multiple slashes.

Related errors


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