{"record":{"id":"b3137e122da18064","repo":"apache/seatunnel","slug":"root-level-schema-cannot-be-used-with-tables-co","errorCode":null,"errorMessage":"root-level 'schema' cannot be used with 'tables_configs'","messagePattern":"root-level 'schema' cannot be used with 'tables_configs'","errorType":"validation","errorClass":"OptionValidationException","httpStatus":null,"severity":"error","filePath":"seatunnel-connectors-v2/connector-neo4j/src/main/java/org/apache/seatunnel/connectors/seatunnel/neo4j/source/Neo4jSourceFactory.java","lineNumber":196,"sourceCode":"                throw new OptionValidationException(\n                        \"'schema' must be configured when using a root-level 'query'\");\n            }\n            return true;\n        }\n    }\n\n    static class TableConfigsValidator implements ConditionExtension<List<Map<String, Object>>> {\n\n        @Override\n        public String description() {\n            return \"each 'tables_configs' entry must contain a non-blank 'query' and a schema with a unique 'table'\";\n        }\n\n        @Override\n        public boolean evaluate(ReadonlyConfig config, List<Map<String, Object>> entries)\n                throws OptionValidationException {\n            if (config.getOptional(ConnectorCommonOptions.SCHEMA).isPresent()) {\n                throw new OptionValidationException(\n                        \"root-level 'schema' cannot be used with 'tables_configs'\");\n            }\n\n            Set<String> tableIds = new HashSet<>();\n            for (int i = 0; i < entries.size(); i++) {\n                Map<String, Object> entry = entries.get(i);\n                Object query = entry.get(Neo4jSourceOptions.KEY_QUERY.key());\n                if (!(query instanceof String) || ((String) query).trim().isEmpty()) {\n                    throw new OptionValidationException(\n                            \"tables_configs[%d]: 'query' must be configured and non-blank\", i);\n                }\n\n                Object schemaValue = entry.get(ConnectorCommonOptions.SCHEMA.key());\n                if (!(schemaValue instanceof Map) || ((Map<?, ?>) schemaValue).isEmpty()) {\n                    throw new OptionValidationException(\n                            \"tables_configs[%d]: 'schema' must be configured and non-empty\", i);\n                }\n","sourceCodeStart":178,"sourceCodeEnd":214,"githubUrl":"https://github.com/apache/seatunnel/blob/cf67b549a7a6c35fa0beb12d83c62892427ea919/seatunnel-connectors-v2/connector-neo4j/src/main/java/org/apache/seatunnel/connectors/seatunnel/neo4j/source/Neo4jSourceFactory.java#L178-L214","documentation":"Neo4jSourceFactory disallows combining a root-level 'schema' option with 'tables_configs'. The two are mutually exclusive configuration modes: root-level query+schema defines a single table, while tables_configs defines multiple tables each with their own query and schema. A root-level schema alongside tables_configs is ambiguous and rejected with OptionValidationException.","triggerScenarios":"Calling evaluate(ReadonlyConfig, List) during validation when the config contains both ConnectorCommonOptions.SCHEMA at the root and a non-empty 'tables_configs' list.","commonSituations":"Users incrementally migrate a single-query config to multi-table config and leave the old root-level schema in place; templated configs merge root-level defaults (schema) with per-table entries.","solutions":["Remove the root-level 'schema' option when using 'tables_configs'; each entry must carry its own schema.","If you only have one table, remove 'tables_configs' and keep the root-level 'query' + 'schema' instead.","Move column/table definitions from the root schema into each tables_configs entry's schema."],"exampleFix":"// before\nsource {\n  Neo4j {\n    uri = \"bolt://localhost:7687\"\n    schema = { table = \"person\", columns = [...] }\n    tables_configs = [\n      { query = \"MATCH (n:Person) RETURN ...\", schema = { table = \"person\", ... } }\n    ]\n  }\n}\n// after\nsource {\n  Neo4j {\n    uri = \"bolt://localhost:7687\"\n    tables_configs = [\n      { query = \"MATCH (n:Person) RETURN ...\", schema = { table = \"person\", columns = [...] } }\n    ]\n  }\n}","handlingStrategy":"validation","validationCode":"if (config.hasPath(\"schema\") && config.hasPath(\"tables_configs\")) {\n  throw new IllegalArgumentException(\"use either root-level schema or tables_configs, not both\");\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Pick one mode: single-table (root query+schema) or multi-table (tables_configs).","When migrating configs, delete the old root-level schema after moving definitions into entries.","Keep config templates per mode to avoid mixing."],"tags":["neo4j","config-validation","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-23T08:17:48.524Z"}