apache/beam · error · SqlUtil.newContextException

Function is already defined.

Error message

Function %s is already defined.

What it means

Thrown by CREATE FUNCTION when a function with the same name already exists in the target schema (schema.getFunctions(lastName) is non-empty). Beam does not allow redefinition of a UDF name within one schema, so it raises a parse-context internal error naming the function.

Solutions

  1. DROP FUNCTION fname first, then re-create it
  2. Use a different function name
  3. Guard DDL scripts to run only once or check function existence beforehand

Example fix

// before
CREATE FUNCTION my_udf AS 'com.x.MyUdf' USING JAR 'gs://bucket/udf.jar';
// after
DROP FUNCTION IF EXISTS my_udf;
CREATE FUNCTION my_udf AS 'com.x.MyUdf' USING JAR 'gs://bucket/udf.jar';
Defensive patterns

Strategy: validation

Validate before calling

boolean fnExists = !schema.getFunctions("my_udf").isEmpty();
if (fnExists) { /* skip or drop first */ }

Prevention

When it happens

Trigger: `CREATE FUNCTION fname ...` where fname is already registered in the same schema — e.g. re-running a setup script or re-declaring a built-in/previous UDF name without dropping it first.

Common situations: Idempotent DDL scripts executed twice; name collision with a Beam built-in SQL function; re-registering a UDF after changing its jar without dropping the old one.

Understand the failure class

Background: "already exists" / EEXIST / FileAlreadyExistsException: what the 'file already exists' error means and how to fix it — this error's family across 37 libraries.

Related errors


AI-assisted analysis of apache/beam@12126d8942 (2026-09-13). Data as JSON: /api/errors/fa87f1289e7612f2. Report an issue: GitHub.

Appendix: source

Thrown at sdks/java/extensions/sql/src/main/java/org/apache/beam/sdk/extensions/sql/impl/parser/SqlCreateFunction.java:98

  }

  @Override
  public SqlOperator getOperator() {
    return OPERATOR;
  }

  @Override
  public List<SqlNode> getOperandList() {
    return Arrays.asList(functionName, jarPath);
  }

  @Override
  public void execute(CalcitePrepare.Context context) {
    final Pair<CalciteSchema, String> pair = SqlDdlNodes.schema(context, true, functionName);
    SchemaPlus schema = pair.left.plus();
    String lastName = pair.right;
    if (!schema.getFunctions(lastName).isEmpty()) {
      throw SqlUtil.newContextException(
          functionName.getParserPosition(),
          RESOURCE.internal(String.format("Function %s is already defined.", lastName)));
    }
    JavaUdfLoader udfLoader = new JavaUdfLoader();
    // TODO(https://github.com/apache/beam/issues/20834) Support qualified function names.
    List<String> functionPath = ImmutableList.of(lastName);
    if (!(jarPath instanceof SqlCharStringLiteral)) {
      throw SqlUtil.newContextException(
          jarPath.getParserPosition(),
          RESOURCE.internal("Jar path is not instanceof SqlCharStringLiteral."));
    }
    String unquotedJarPath = ((SqlCharStringLiteral) jarPath).getNlsString().getValue();
    if (isAggregate) {
      // Try loading the aggregate function just to make sure it exists. LazyAggregateCombineFn will
      // need to fetch it again at runtime.
      udfLoader.loadAggregateFunction(functionPath, unquotedJarPath);
      LazyAggregateCombineFn<?, ?, ?> combineFn =
          new LazyAggregateCombineFn<>(functionPath, unquotedJarPath);

View on GitHub (pinned to 12126d8942)