apache/seatunnel · error · FactoryException

sourceOptionRule can not be null

Error message

sourceOptionRule can not be null

What it means

FactoryUtil.sourceFullOptionRule throws this FactoryException when the given TableSourceFactory's optionRule() returns null. SeaTunnel uses the option rule to expose full source configuration (e.g. to SeaTunnel Web), and a null rule is treated as a broken factory implementation.

Solutions

  1. Implement optionRule() in the TableSourceFactory to return a non-null OptionRule (OptionRule.builder()...build()).
  2. Upgrade the connector jar to a version that declares an option rule.
  3. If the connector genuinely has no options, return an empty OptionRule instead of null.

Example fix

// before
@Override
public OptionRule optionRule() { return null; }
// after
@Override
public OptionRule optionRule() {
    return OptionRule.builder().required(DataSourceOptions.URL).optional(DataSourceOptions.USERNAME).build();
}
Defensive patterns

Strategy: type-guard

Validate before calling

OptionRule rule = factory.optionRule();
if (rule == null) {
    throw new IllegalStateException(factory.factoryIdentifier() + " must implement optionRule()");
}

Type guard

boolean hasValidOptionRule(TableSourceFactory f) { return f.optionRule() != null; }

Try / catch

try {
    return FactoryUtil.sourceFullOptionRule(factory);
} catch (FactoryException e) {
    throw new IllegalStateException("Connector must declare a non-null OptionRule", e);
}

Prevention

When it happens

Trigger: Calling FactoryUtil.sourceFullOptionRule(factory) where factory is a custom/incomplete TableSourceFactory that overrides optionRule() to return null, or an old connector not yet migrated to declare an OptionRule.

Common situations: Developing a custom source connector and forgetting to implement optionRule(); using an outdated third-party connector jar against a newer API that requires a non-null option rule.

Related errors


AI-assisted analysis of apache/seatunnel@cf67b549a7 (2026-09-10). Data as JSON: /api/errors/c65c3a6a1abcd5de. Report an issue: GitHub.

Appendix: source

Thrown at seatunnel-api/src/main/java/org/apache/seatunnel/api/table/factory/FactoryUtil.java:421

        try {
            final List<Factory> result = new LinkedList<>();
            ServiceLoader.load(Factory.class, classLoader).iterator().forEachRemaining(result::add);
            return result;
        } catch (ServiceConfigurationError e) {
            LOG.error("Could not load service provider for factories.", e);
            throw new FactoryException("Could not load service provider for factories.", e);
        }
    }

    /**
     * This method is called by SeaTunnel Web to get the full option rule of a source.
     *
     * @return Option rule
     */
    public static OptionRule sourceFullOptionRule(@NonNull TableSourceFactory factory) {
        OptionRule sourceOptionRule = factory.optionRule();
        if (sourceOptionRule == null) {
            throw new FactoryException("sourceOptionRule can not be null");
        }

        Class<? extends SeaTunnelSource> sourceClass = factory.getSourceClass();
        if (factory instanceof SupportParallelism
                // TODO: Implement SupportParallelism in the TableSourceFactory instead of the
                // SeaTunnelSource
                || SupportParallelism.class.isAssignableFrom(sourceClass)) {
            OptionRule sourceCommonOptionRule =
                    OptionRule.builder().optional(EnvCommonOptions.PARALLELISM).build();
            sourceOptionRule
                    .getOptionalOptions()
                    .addAll(sourceCommonOptionRule.getOptionalOptions());
        }

        return sourceOptionRule;
    }

    /**

View on GitHub (pinned to cf67b549a7)