{"record":{"id":"182bf35ed9c433fb","repo":"apache/seatunnel","slug":"operator-s-requires-a-compareoption-cross-field","errorCode":null,"errorMessage":"Operator %s requires a compareOption (cross-field comparison), but compareOption is null","messagePattern":"Operator (.+?) requires a compareOption \\(cross-field comparison\\), but compareOption is null","errorType":"validation","errorClass":"java.lang.IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"seatunnel-api/src/main/java/org/apache/seatunnel/api/configuration/util/Condition.java","lineNumber":61,"sourceCode":"    Condition(\n            Option<T> option, ConditionOperator operator, T expectValue, Option<?> compareOption) {\n        this(option, operator, expectValue, compareOption, null);\n    }\n\n    Condition(\n            Option<T> option,\n            ConditionOperator operator,\n            T expectValue,\n            Option<?> compareOption,\n            ConditionExtension<T> extension) {\n        if (option == null) {\n            throw new IllegalArgumentException(\"Condition option must not be null\");\n        }\n        if (operator == null) {\n            throw new IllegalArgumentException(\"Condition operator must not be null\");\n        }\n        if (operator.getSource() == ConditionOperator.Source.FIELD && compareOption == null) {\n            throw new IllegalArgumentException(\n                    String.format(\n                            \"Operator %s requires a compareOption (cross-field comparison), but compareOption is null\",\n                            operator.name()));\n        }\n        if (operator.getArity() == ConditionOperator.Arity.BINARY\n                && operator.getSource() == ConditionOperator.Source.LITERAL\n                && expectValue == null) {\n            throw new IllegalArgumentException(\n                    String.format(\n                            \"Operator %s requires an expectValue, but expectValue is null\",\n                            operator.name()));\n        }\n        if (operator == ConditionOperator.EXTENSION && extension == null) {\n            throw new IllegalArgumentException(\n                    \"Operator EXTENSION requires a non-null ConditionExtension\");\n        }\n        this.option = option;\n        this.operator = operator;","sourceCodeStart":43,"sourceCodeEnd":79,"githubUrl":"https://github.com/apache/seatunnel/blob/cf67b549a7a6c35fa0beb12d83c62892427ea919/seatunnel-api/src/main/java/org/apache/seatunnel/api/configuration/util/Condition.java#L43-L79","documentation":"ExcelReadStrategy.setCatalogTable extracts the SeaTunnelRowType from the provided CatalogTable and requires non-empty fieldNames and fieldTypes; the Excel reader cannot infer a schema by itself. If either array is null or empty it throws FileConnectorException with UNSUPPORTED_OPERATION, because subsequent row construction depends entirely on the user-declared schema.","triggerScenarios":"setCatalogTable is called (by the source reader initialization or tests like testExcelRead) with a CatalogTable whose SeaTunnelRowType has null/empty field names or field types — typically a CatalogTable built without schema information.","commonSituations":"Programmatically building a CatalogTable/SeaTunnelRowType and forgetting to populate fields; upstream pipeline passing an unschema'd CatalogTable to the Excel source; test fixtures with empty schema arrays.","solutions":["Define schema in the source config so the CatalogTable carries field names and types (schema block in the Excel source configuration).","When constructing CatalogTable programmatically, populate SeaTunnelRowType with matching, non-empty fieldNames and fieldTypes arrays of equal length.","Verify the upstream component that produces the CatalogTable actually resolves the table schema before calling setCatalogTable."],"exampleFix":"// before\nSeaTunnelRowType rowType = new SeaTunnelRowType(new String[]{}, new SeaTunnelType[]{}, new int[]{});\n// after\nSeaTunnelRowType rowType = new SeaTunnelRowType(\n    new String[]{\"name\", \"age\"},\n    new SeaTunnelType[]{BasicType.STRING_TYPE, BasicType.INT_TYPE},\n    new int[]{0, 1});","handlingStrategy":"validation","validationCode":"java\nSeaTunnelRowType rowType = catalogTable.getSeaTunnelRowType();\nif (rowType.getFieldNames() == null || rowType.getFieldNames().length == 0\n        || rowType.getFieldTypes() == null || rowType.getFieldTypes().length == 0) {\n    throw new IllegalArgumentException(\n        \"Excel source requires a schema: define field names and types in the source config\");\n}","typeGuard":"java\nstatic boolean hasValidSchema(SeaTunnelRowType rowType) {\n    return rowType != null\n        && rowType.getFieldNames() != null && rowType.getFieldNames().length > 0\n        && rowType.getFieldTypes() != null && rowType.getFieldTypes().length > 0;\n}","tryCatchPattern":"java\ntry {\n    strategy.setCatalogTable(catalogTable);\n} catch (FileConnectorException e) {\n    if (e.getMessage().contains(\"Schema information is not set\")) {\n        LOG.error(\"Provide a schema for the Excel source (field names + types)\", e);\n    }\n}","preventionTips":["Always declare the schema block for Excel sources — inference is not supported","When building CatalogTable programmatically, assert fieldNames.length == fieldTypes.length > 0","Resolve upstream table schema before handing the CatalogTable to the reader"],"tags":["excel","schema","catalog-table","connector-file"],"backgroundTag":"missing-required-config-field","analyzedSha":"cf67b549a7a6c35fa0beb12d83c62892427ea919","analyzedAt":"2026-09-10T21:44:55.265Z","contentChangedAt":"2026-09-10T21:44:55.265Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}