{"record":{"id":"0a90931ad6e27737","repo":"apache/seatunnel","slug":"the-sql-config-must-contain-at-least-one-source-ta","errorCode":null,"errorMessage":"The SQL config must contain at least one source table","messagePattern":"The SQL config must contain at least one source table","errorType":"exception","errorClass":"ParserException","httpStatus":null,"severity":"error","filePath":"seatunnel-config/seatunnel-config-sql/src/main/java/org/apache/seatunnel/config/sql/SqlConfigBuilder.java","lineNumber":155,"sourceCode":"            }\n\n            // filter out the sink config without 'plugin_input' option\n            seaTunnelConfig.setSinkConfigs(\n                    seaTunnelConfig.getSinkConfigs().stream()\n                            .filter(\n                                    sinkConfig -> {\n                                        boolean containSourceTable = false;\n                                        for (Option option : sinkConfig.getOptions()) {\n                                            if (option.getKey().equals(OPTION_PLUGIN_INPUT_KEY)) {\n                                                containSourceTable = true;\n                                                break;\n                                            }\n                                        }\n                                        return containSourceTable;\n                                    })\n                            .collect(Collectors.toList()));\n            if (seaTunnelConfig.getSourceConfigs().isEmpty()) {\n                throw new ParserException(\"The SQL config must contain at least one source table\");\n            }\n            if (seaTunnelConfig.getSinkConfigs().isEmpty()) {\n                throw new ParserException(\n                        \"The SQL config must contain `INSERT INTO ... SELECT ...` syntax\");\n            }\n\n            // render to hocon config\n            String configContent = ConfigTemplate.generate(seaTunnelConfig);\n            log.debug(\"Generated config: \\n{}\", configContent);\n            return ConfigFactory.parseString(configContent);\n        } catch (ParserException e) {\n            throw e;\n        } catch (Exception e) {\n            throw new ParserException(e);\n        }\n    }\n\n    private static List<String> parseAnnoConfigAndSqlLine(","sourceCodeStart":137,"sourceCodeEnd":173,"githubUrl":"https://github.com/apache/seatunnel/blob/cf67b549a7a6c35fa0beb12d83c62892427ea919/seatunnel-config/seatunnel-config-sql/src/main/java/org/apache/seatunnel/config/sql/SqlConfigBuilder.java#L137-L173","documentation":"After parsing all statements, SqlConfigBuilder requires at least one table whose 'type' option is 'source'; if sourceConfigs is empty it throws ParserException. Every job needs at least one source table to read from.","triggerScenarios":"The .sql file defines only sink tables (CREATE TABLE ... WITH ('type'='sink')) plus an INSERT whose SELECT reads from tables never declared as sources.","commonSituations":"Forgetting the 'type'='source' option on the source CREATE TABLE, mistyping it ('Source', 'src'), SELECTing against a table never created, or sources being misclassified due to wrong 'type' values.","solutions":["Add a CREATE TABLE ... WITH ('connector'='...', 'type'='source') for the read-side table.","Verify the type option value is exactly 'source' (case-insensitive).","Ensure the INSERT's SELECT references the declared source table by name.","Check the source table wasn't parsed as a sink due to a wrong 'type' value."],"exampleFix":"// before\nCREATE TABLE sink WITH ('connector'='console', 'type'='sink') (id INT);\nINSERT INTO sink SELECT id FROM src; -- src never declared\n// after\nCREATE TABLE src WITH ('connector'='fake', 'type'='source') (id INT);\nCREATE TABLE sink WITH ('connector'='console', 'type'='sink') (id INT);\nINSERT INTO sink SELECT id FROM src;","handlingStrategy":"validation","validationCode":"boolean hasSource = Files.readAllLines(sqlFile).stream()\n    .anyMatch(l -> l.contains(\"'type'='source'\") || l.contains(\"'type' = 'source'\"));","typeGuard":null,"tryCatchPattern":"try { Config c = SqlConfigBuilder.of(sqlFile); } catch (ParserException e) { if (e.getMessage().contains(\"at least one source\")) { /* add source table and retry */ } }","preventionTips":["Always declare source tables with 'type'='source'","Spell option values exactly ('source'/'sink')","Ensure INSERT SELECT references declared source tables","Lint config: at least one source and one sink"],"tags":["sql-config","config-validation","missing-source","java"],"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"}