apache/beam · error · SqlUtil.newContextException

Jar path is not instanceof SqlCharStringLiteral.

Error message

Jar path is not instanceof SqlCharStringLiteral.

What it means

CREATE FUNCTION requires the USING JAR path to be a string literal (SqlCharStringLiteral). If the jarPath operand parses as any other SqlNode kind (e.g. an identifier, expression, or non-string literal), execute() raises an internal error instead of silently coercing.

Solutions

  1. Quote the jar path as a single-quoted SQL string literal: USING JAR 'gs://bucket/udf.jar'
  2. Avoid double quotes or backticks around the path (they may parse as identifiers)
  3. When generating SQL, emit the path via string-literal escaping

Example fix

// before
CREATE FUNCTION udf AS 'com.x.Udf' USING JAR "gs://bucket/udf.jar";
// after
CREATE FUNCTION udf AS 'com.x.Udf' USING JAR 'gs://bucket/udf.jar';
Defensive patterns

Strategy: validation

Validate before calling

// Ensure the JAR path is emitted as a single-quoted string literal in generated SQL
String ddl = "CREATE FUNCTION " + fn + " AS '" + cls + "' USING JAR '" + jarPath.replace("'", "''") + "'";

Prevention

When it happens

Trigger: Writing the jar location as a quoted identifier, a column-style reference, or any non-string-literal expression in CREATE FUNCTION ... USING JAR, e.g. backticked or unquoted paths.

Common situations: Confusing SQL dialect quoting rules (double quotes make identifiers in some dialects); generating DDL programmatically and emitting the path unquoted; copying syntax from non-Beam engines.

Understand the failure class

Background: "Invalid ... format", "must be in format X", "does not look like a ..." — invalid argument format errors across CLI tools and libraries — this error's family across 17 libraries.

Related errors


AI-assisted analysis of apache/beam@12126d8942 (2026-09-13). Data as JSON: /api/errors/4fa0c411d8739fe3. 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:106

  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);
      schema.add(lastName, combineFn.getUdafImpl());
    } else {
      ScalarFn scalarFn = udfLoader.loadScalarFunction(functionPath, unquotedJarPath);
      Method method = ScalarFnReflector.getApplyMethod(scalarFn);
      Function function = ScalarFunctionImpl.create(method, unquotedJarPath);
      schema.add(lastName, function);
    }
  }

View on GitHub (pinned to 12126d8942)