apache/beam · error · RuntimeException

must not throw checked exception

Error message

 must not throw checked exception

What it means

ScalarFunctionImpl.validateMethod checks a candidate UDF method before wrapping it in a Calcite function: its declaring class must have a public zero-arg constructor and the method must declare no exceptions. A method that declares checked (or any) exception types would leak exceptions through Calcite's invoke path, so create() fails with a RuntimeException naming the method.

Source

Thrown at sdks/java/extensions/sql/src/main/java/org/apache/beam/sdk/extensions/sql/impl/ScalarFunctionImpl.java:135

   * @param method method that is used to implement the function
   * @param jarPath Path to jar that contains the method.
   * @return created {@link Function}
   */
  public static Function create(Method method, String jarPath) {
    validateMethod(method);
    CallImplementor implementor = createImplementor(method);
    return new ScalarFunctionImpl(method, implementor, jarPath);
  }

  protected static void validateMethod(Method method) {
    if (!Modifier.isStatic(method.getModifiers())) {
      Class clazz = method.getDeclaringClass();
      if (!classHasPublicZeroArgsConstructor(clazz)) {
        throw RESOURCE.requireDefaultConstructor(clazz.getName()).ex();
      }
    }
    if (method.getExceptionTypes().length != 0) {
      throw new RuntimeException(method.getName() + " must not throw checked exception");
    }
  }

  @Override
  public RelDataType getReturnType(RelDataTypeFactory typeFactory) {
    return CalciteUtils.sqlTypeWithAutoCast(typeFactory, method.getGenericReturnType());
  }

  @Override
  public CallImplementor getImplementor() {
    return implementor;
  }

  /**
   * Version of {@link ReflectiveCallNotNullImplementor} that does parameter conversion for Beam
   * UDFs.
   */
  private static class ScalarReflectiveCallNotNullImplementor

View on GitHub (pinned to 12126d8942)

Solutions

  1. Remove the throws clause and catch the checked exception inside the method, rethrowing as an unchecked exception.
  2. Handle the failure in-band, e.g. return null for unparseable input instead of throwing.
  3. Wrap the library call in try/catch and convert to RuntimeException with context if the UDF should fail the query.
  4. Ensure the declaring class also has a public no-arg constructor (the other requirement of validateMethod).

Example fix

// before
public LocalDate parse(String s) throws ParseException {
  return new SimpleDateFormat("yyyy-MM-dd").parse(s).toInstant()...;
}
// after
public LocalDate parse(String s) {
  try {
    return LocalDate.parse(s);
  } catch (DateTimeParseException e) {
    throw new IllegalArgumentException("bad date: " + s, e);
  }
}
Defensive patterns

Strategy: validation

Validate before calling

static void checkNoCheckedExceptions(java.lang.reflect.Method m) {
  if (m.getExceptionTypes().length > 0)
    throw new IllegalStateException(m.getName() + " must not declare exceptions");
  if (!java.lang.reflect.Modifier.isPublic(m.getDeclaringClass().getModifiers())
      || java.util.Arrays.stream(m.getDeclaringClass().getConstructors())
          .noneMatch(c -> c.getParameterCount() == 0))
    throw new IllegalStateException(m.getDeclaringClass() + " needs a public zero-arg constructor");
}

Try / catch

try {
  ScalarFunctionImpl.create(method);
} catch (RuntimeException e) {
  if (e.getMessage().contains("must not throw checked exception")) {
    throw new IllegalStateException("Remove throws clause / catch inside the UDF method", e);
  }
  throw e;
}

Prevention

When it happens

Trigger: Registering a scalar function via ScalarFunctionImpl.create whose method signature ends in 'throws SomeException' — the exception types array is non-empty at validateMethod.

Common situations: Wrapping an existing library call that throws checked exceptions (IOException, ParseException, SQLException) directly as a UDF; inheriting a method signature with throws clauses; auto-generating UDF stubs from interfaces with throws clauses.

Understand the failure class

Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.

Related errors


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