{"record":{"id":"6386e1571c7d9a54","repo":"apache/seatunnel","slug":"unsupported-required-option-type-requiredoption","errorCode":null,"errorMessage":"Unsupported required option type: ${requiredOptionClassName}","messagePattern":"Unsupported required option type: (.+?)","errorType":"validation","errorClass":"IllegalArgumentException","httpStatus":400,"severity":"error","filePath":"seatunnel-engine/seatunnel-engine-server/src/main/java/org/apache/seatunnel/engine/server/rest/service/OptionRulesService.java","lineNumber":231,"sourceCode":"    }\n\n    private String normalizePluginName(String pluginName) {\n        if (StringUtils.isBlank(pluginName)) {\n            throw new IllegalArgumentException(\n                    String.format(\"Parameter '%s' cannot be empty.\", PARAM_PLUGIN));\n        }\n        return pluginName.trim().toLowerCase(Locale.ROOT);\n    }\n\n    private OptionRuleResponse.RequiredOptionMetadata toRequiredOptionMetadata(\n            RequiredOption requiredOption) {\n        List<OptionRuleResponse.OptionMetadata> options =\n                requiredOption.getOptions().stream()\n                        .map(this::toOptionMetadata)\n                        .collect(Collectors.toList());\n        OptionRuleResponse.RuleType ruleType = resolveRuleType(requiredOption);\n        if (ruleType == null) {\n            throw new IllegalArgumentException(\n                    String.format(\n                            \"Unsupported required option type: %s\",\n                            requiredOption.getClass().getName()));\n        }\n        if (requiredOption instanceof RequiredOption.ConditionalRequiredOptions) {\n            Expression expression =\n                    ((RequiredOption.ConditionalRequiredOptions) requiredOption).getExpression();\n            return new OptionRuleResponse.RequiredOptionMetadata(\n                    ruleType, options, expression.toString(), toExpressionNode(expression));\n        }\n        return new OptionRuleResponse.RequiredOptionMetadata(ruleType, options, null, null);\n    }\n\n    private OptionRuleResponse.ConditionRuleMetadata toConditionRuleMetadata(\n            ConditionRule conditionRule) {\n        Expression expression = conditionRule.getExpression();\n        return new OptionRuleResponse.ConditionRuleMetadata(\n                expression.toString(),","sourceCodeStart":213,"sourceCodeEnd":249,"githubUrl":"https://github.com/apache/seatunnel/blob/cf67b549a7a6c35fa0beb12d83c62892427ea919/seatunnel-engine/seatunnel-engine-server/src/main/java/org/apache/seatunnel/engine/server/rest/service/OptionRulesService.java#L213-L249","documentation":"toRequiredOptionMetadata converts a RequiredOption instance into REST response metadata. It resolves a RuleType via resolveRuleType(requiredOption); if the resolver returns null the RequiredOption implementation class is not one of the known types (e.g. MultiChoiceOption, ConditionalRequiredOptions), so an IllegalArgumentException with the class name is thrown. This is an internal invariant against unhandled RequiredOption implementations.","triggerScenarios":"Rendering option rules for a plugin whose OptionRule contains a RequiredOption subclass that resolveRuleType does not map — typically after a new RequiredOption type is added to seatunnel-api without updating OptionRulesService.","commonSituations":"SeaTunnel core upgraded with a new option-rule type while the engine-server REST module (or its dependency version) is stale; a custom plugin returns a bespoke RequiredOption implementation; mixed jar versions in the Zeta cluster.","solutions":["Upgrade the engine server so OptionRulesService is in sync with the seatunnel-api version producing the OptionRule","Add a mapping for the new RequiredOption type in resolveRuleType and handle it in toRequiredOptionMetadata","Inspect the class name in the message and file an issue/patch in OptionRulesService.java around line 231","Check for mixed jar versions of seatunnel-api on the server classpath"],"exampleFix":"// before (in resolveRuleType)\nif (requiredOption instanceof RequiredOption.RequiredOptions) return ...;\n// after — add the missing type\nif (requiredOption instanceof MyNewRequiredOption) {\n    return OptionRuleResponse.RuleType.MY_NEW_TYPE;\n}","handlingStrategy":"try-catch","validationCode":"// ensure engine-server and seatunnel-api versions match\nassert OptionRulesService.class.getPackage() != null; // and check dependency tree","typeGuard":"boolean supported(RequiredOption o) { return o instanceof RequiredOption.RequiredOptions || o instanceof RequiredOption.ConditionalRequiredOptions || o instanceof MultiChoiceOption; }","tryCatchPattern":"try { renderRule(rule); } catch (IllegalArgumentException e) { log.error(\"Unsupported RequiredOption: {}\", e.getMessage()); return fallbackMetadata(); }","preventionTips":["Keep engine-server and seatunnel-api on the same release","Add a resolveRuleType branch whenever a new RequiredOption type is introduced","Add a unit test covering all RequiredOption implementations"],"tags":["rest","internal","unsupported-type","option-rules"],"backgroundTag":"unsupported-operation","analyzedSha":"cf67b549a7a6c35fa0beb12d83c62892427ea919","analyzedAt":"2026-09-10T21:44:55.265Z","contentChangedAt":"2026-09-10T21:44:55.265Z","schemaVersion":2},"datasetVersion":"2026-09-14T21:17:11.552Z"}