{"record":{"id":"ce7a0f0f6a8f0791","repo":"apache/seatunnel","slug":"sinkoptionrule-can-not-be-null","errorCode":null,"errorMessage":"sinkOptionRule can not be null","messagePattern":"sinkOptionRule can not be null","errorType":"validation","errorClass":"FactoryException","httpStatus":null,"severity":"error","filePath":"seatunnel-api/src/main/java/org/apache/seatunnel/api/table/factory/FactoryUtil.java","lineNumber":447,"sourceCode":"            OptionRule sourceCommonOptionRule =\n                    OptionRule.builder().optional(EnvCommonOptions.PARALLELISM).build();\n            sourceOptionRule\n                    .getOptionalOptions()\n                    .addAll(sourceCommonOptionRule.getOptionalOptions());\n        }\n\n        return sourceOptionRule;\n    }\n\n    /**\n     * This method is called by SeaTunnel Web to get the full option rule of a sink.\n     *\n     * @return Option rule\n     */\n    public static OptionRule sinkFullOptionRule(@NonNull TableSinkFactory factory) {\n        OptionRule sinkOptionRule = factory.optionRule();\n        if (sinkOptionRule == null) {\n            throw new FactoryException(\"sinkOptionRule can not be null\");\n        }\n        return sinkOptionRule;\n    }\n\n    public static SeaTunnelTransform<?> createAndPrepareMultiTableTransform(\n            List<CatalogTable> catalogTables,\n            ReadonlyConfig options,\n            ClassLoader classLoader,\n            String factoryIdentifier) {\n        final TableTransformFactory factory =\n                discoverFactory(classLoader, TableTransformFactory.class, factoryIdentifier);\n        TableTransformFactoryContext context =\n                new TableTransformFactoryContext(catalogTables, options, classLoader);\n        ConfigValidator.of(context.getOptions()).validate(factory.optionRule());\n        return factory.createTransform(context).createTransform();\n    }\n\n    private static <T extends Factory> boolean isFallback(","sourceCodeStart":429,"sourceCodeEnd":465,"githubUrl":"https://github.com/apache/seatunnel/blob/cf67b549a7a6c35fa0beb12d83c62892427ea919/seatunnel-api/src/main/java/org/apache/seatunnel/api/table/factory/FactoryUtil.java#L429-L465","documentation":"FactoryUtil.sinkFullOptionRule throws this FactoryException when the given TableSinkFactory's optionRule() returns null. Like the source variant, a null rule means the factory does not satisfy the API contract of exposing its configuration options.","triggerScenarios":"Calling FactoryUtil.sinkFullOptionRule(factory) with a sink factory whose optionRule() is unimplemented or explicitly returns null — typical of custom or legacy connectors.","commonSituations":"Custom sink connector under development missing optionRule(); third-party sink jar built against an older API; copying a sink skeleton that left optionRule() returning null.","solutions":["Implement optionRule() in the TableSinkFactory returning a non-null OptionRule.","Upgrade the sink connector to a release that declares its option rule.","Return an empty OptionRule if the sink truly has no user options."],"exampleFix":"// before\n@Override\npublic OptionRule optionRule() { return null; }\n// after\n@Override\npublic OptionRule optionRule() {\n    return OptionRule.builder().required(SinkOptions.JDBC_URL).build();\n}","handlingStrategy":"type-guard","validationCode":"OptionRule rule = factory.optionRule();\nif (rule == null) {\n    throw new IllegalStateException(factory.factoryIdentifier() + \" must implement optionRule()\");\n}","typeGuard":"boolean hasValidOptionRule(TableSinkFactory f) { return f.optionRule() != null; }","tryCatchPattern":"try {\n    return FactoryUtil.sinkFullOptionRule(factory);\n} catch (FactoryException e) {\n    throw new IllegalStateException(\"Sink connector must declare a non-null OptionRule\", e);\n}","preventionTips":["Implement optionRule() in every custom TableSinkFactory.","Add a test asserting optionRule() is non-null for each sink connector.","Pin sink connector versions known to declare option rules."],"tags":["option-rule","factory","api-contract"],"backgroundTag":"method-not-implemented","analyzedSha":"cf67b549a7a6c35fa0beb12d83c62892427ea919","analyzedAt":"2026-09-10T21:44:55.265Z","contentChangedAt":"2026-09-10T21:44:55.265Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}