{"record":{"id":"3eea933890b42653","repo":"apache/seatunnel","slug":"schema-must-be-configured-when-using-a-root-leve","errorCode":null,"errorMessage":"'schema' must be configured when using a root-level 'query'","messagePattern":"'schema' must be configured when using a root-level 'query'","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":178,"sourceCode":"\n    private static Neo4jConnectorException configError(String message) {\n        return new Neo4jConnectorException(SeaTunnelAPIErrorCode.CONFIG_VALIDATION_FAILED, message);\n    }\n\n    static class SingleTableConfigValidator implements ConditionExtension<String> {\n\n        @Override\n        public String description() {\n            return \"'schema' must be configured when using a root-level 'query'\";\n        }\n\n        @Override\n        public boolean evaluate(ReadonlyConfig config, String query)\n                throws OptionValidationException {\n            Map<String, Object> schema =\n                    config.getOptional(ConnectorCommonOptions.SCHEMA).orElse(null);\n            if (schema == null || schema.isEmpty()) {\n                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(","sourceCodeStart":160,"sourceCodeEnd":196,"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#L160-L196","documentation":"Neo4jSourceFactory's root-level query validator requires a 'schema' block whenever a root-level 'query' is used in the Neo4j source config. Without an explicit schema, the connector cannot map the Cypher query result columns to SeaTunnel catalog columns (table name, columns, types). It throws OptionValidationException during option validation, before the job starts.","triggerScenarios":"Configuring a Neo4j source with a root-level 'query' option (Cypher statement) while omitting the root-level 'schema' option, or supplying an empty schema map. evaluate(ReadonlyConfig, String) throws when config.getOptional(ConnectorCommonOptions.SCHEMA) is absent or empty.","commonSituations":"Users copy a query-based Neo4j source example but delete the schema section; users migrating from table_configs-style configs forget schema is mandatory with root-level query; auto-generated configs omit schema because the connector cannot infer it from Cypher.","solutions":["Add a root-level 'schema' block with table_name, columns (name/type pairs) matching the Cypher result.","If you don't want a schema, switch to 'tables_configs' with per-entry query + schema instead of a root-level 'query'.","Ensure the schema block is non-empty (an empty map still triggers the error)."],"exampleFix":"// before\nsource {\n  Neo4j {\n    uri = \"bolt://localhost:7687\"\n    query = \"MATCH (n:Person) RETURN n.name AS name, n.age AS age\"\n  }\n}\n// after\nsource {\n  Neo4j {\n    uri = \"bolt://localhost:7687\"\n    query = \"MATCH (n:Person) RETURN n.name AS name, n.age AS age\"\n    schema = {\n      table = \"person\"\n      columns = [\n        { name = name, type = string }\n        { name = age, type = int }\n      ]\n    }\n  }\n}","handlingStrategy":"validation","validationCode":"if (config.hasPath(\"query\") && (!config.hasPath(\"schema\") || config.getConfig(\"schema\").isEmpty())) {\n  throw new IllegalArgumentException(\"root-level 'query' requires a non-empty 'schema'\");\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Always pair a root-level 'query' with a complete schema (table + columns).","Use tables_configs mode if you cannot provide a root schema.","Validate the config locally with a dry-run before submitting the job."],"tags":["neo4j","config-validation","missing-schema"],"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-23T08:17:48.524Z"}