{"record":{"id":"6b5aacb7b1884295","repo":"apache/seatunnel","slug":"schema-config-can-t-contains-both-fields-and-co","errorCode":null,"errorMessage":"Schema config can't contains both [fields] and [columns], please correct your config first","messagePattern":"Schema config can't contains both \\[fields\\] and \\[columns\\], please correct your config first","errorType":"validation","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"seatunnel-api/src/main/java/org/apache/seatunnel/api/table/catalog/schema/ReadonlyConfigParser.java","lineNumber":59,"sourceCode":"    private final TableSchemaParser.ColumnParser<ReadonlyConfig> columnParser = new ColumnParser();\n    private final TableSchemaParser.FieldParser<ReadonlyConfig> fieldParser = new FieldParser();\n    private final TableSchemaParser.ConstraintKeyParser<ReadonlyConfig> constraintKeyParser =\n            new ConstraintKeyParser();\n    private final TableSchemaParser.PrimaryKeyParser<ReadonlyConfig> primaryKeyParser =\n            new PrimaryKeyParser();\n\n    @Override\n    public TableSchema parse(ReadonlyConfig readonlyConfig) {\n        ReadonlyConfig schemaConfig =\n                readonlyConfig\n                        .getOptional(ConnectorCommonOptions.SCHEMA)\n                        .map(ReadonlyConfig::fromMap)\n                        .orElseThrow(\n                                () -> new IllegalArgumentException(\"Schema config can't be null\"));\n\n        if (readonlyConfig.getOptional(ConnectorCommonOptions.FIELDS).isPresent()\n                && schemaConfig.getOptional(ConnectorCommonOptions.COLUMNS).isPresent()) {\n            throw new IllegalArgumentException(\n                    \"Schema config can't contains both [fields] and [columns], please correct your config first\");\n        }\n        TableSchema.Builder tableSchemaBuilder = TableSchema.builder();\n        if (readonlyConfig.getOptional(ConnectorCommonOptions.FIELDS).isPresent()) {\n            // we use readonlyConfig here to avoid flatten, this is used to solve the t.x.x as field\n            // key\n            tableSchemaBuilder.columns(fieldParser.parse(readonlyConfig));\n        }\n\n        if (schemaConfig.getOptional(ConnectorCommonOptions.COLUMNS).isPresent()) {\n            tableSchemaBuilder.columns(columnParser.parse(schemaConfig));\n        }\n        if (schemaConfig.getOptional(ConnectorCommonOptions.PRIMARY_KEY).isPresent()) {\n            tableSchemaBuilder.primaryKey(primaryKeyParser.parse(schemaConfig));\n        }\n        if (schemaConfig.getOptional(ConnectorCommonOptions.CONSTRAINT_KEYS).isPresent()) {\n            tableSchemaBuilder.constraintKey(constraintKeyParser.parse(schemaConfig));\n        }","sourceCodeStart":41,"sourceCodeEnd":77,"githubUrl":"https://github.com/apache/seatunnel/blob/cf67b549a7a6c35fa0beb12d83c62892427ea919/seatunnel-api/src/main/java/org/apache/seatunnel/api/table/catalog/schema/ReadonlyConfigParser.java#L41-L77","documentation":"ReadonlyConfigParser.parse rejects schema configurations that define columns via both the legacy/new 'fields' key and the 'columns' key. The two schema declaration styles are mutually exclusive because they would produce an ambiguous TableSchema. The parser forces users to pick one style.","triggerScenarios":"A source/sink config block contains schema { fields = {...} } and also schema { columns = [...] } (or nested options with both ConnectorCommonOptions.FIELDS and COLUMNS present).","commonSituations":"Merging example configs when migrating between config styles, combining a vendor-provided template with hand-edits, or programmatic config assembly that appends both keys.","solutions":["Remove either the fields block or the columns block from the schema config, keeping one style","If migrating, translate fields entries to the columns format (or vice versa) and delete the old key","Search config files/merge scripts for both keys coexisting in the same schema block"],"exampleFix":"// before\nschema {\n  fields { id = bigint }\n  columns [{ name = id, type = bigint }]\n}\n// after\nschema {\n  columns [{ name = id, type = bigint }]\n}","handlingStrategy":"validation","validationCode":"Map<String,Object> schema = (Map<String,Object>) config.get(\"schema\");\nif (schema != null && schema.containsKey(\"fields\") && schema.containsKey(\"columns\"))\n    throw new IllegalArgumentException(\"Use either schema.fields or schema.columns, not both\");","typeGuard":null,"tryCatchPattern":"try { schema = new ReadonlyConfigParser<>(TableSchema.class).parse(readonlyConfig); } catch (IllegalArgumentException e) { throw new IllegalArgumentException(\"Fix schema config: \" + e.getMessage()); }","preventionTips":["Pick one schema style (fields or columns) per connector config and stick to it","When merging configs, delete superseded schema keys","Grep templates/scripts for both keys in the same schema block"],"tags":["config","schema","conflicting-options"],"backgroundTag":"conflicting-config-options","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"}