{"record":{"id":"5ff0b39054955ae2","repo":"apache/seatunnel","slug":"location-sink-configindex-factoryid-does-not","errorCode":null,"errorMessage":"${location(SINK, configIndex, factoryId)} does not support writing both a multi-table source and other sources.","messagePattern":"(.+?) does not support writing both a multi-table source and other sources\\.","errorType":"validation","errorClass":"ConfigCheckException","httpStatus":null,"severity":"error","filePath":"seatunnel-core/seatunnel-starter/src/main/java/org/apache/seatunnel/core/starter/seatunnel/command/DryRunConnectValidator.java","lineNumber":368,"sourceCode":"                            PluginType.SINK,\n                            configIndex,\n                            factoryId,\n                            \"connector dry-run connection validation passed\"));\n        } catch (Exception e) {\n            throw wrap(PluginType.SINK, configIndex, factoryId, e);\n        }\n    }\n\n    /**\n     * Flattens sink input vertices to the catalog tables the sink must accept, enforcing the same\n     * multi-input constraints as the runtime parser.\n     */\n    private List<CatalogTable> resolveSinkInputTables(\n            List<SchemaInfo> inputVertices, int configIndex, String factoryId) {\n        if (inputVertices.size() > 1) {\n            for (SchemaInfo inputVertex : inputVertices) {\n                if (inputVertex.getCatalogTables().size() > 1) {\n                    throw new ConfigCheckException(\n                            location(PluginType.SINK, configIndex, factoryId)\n                                    + \" does not support writing both a multi-table source and other sources.\");\n                }\n            }\n            List<CatalogTable> mergedInputs =\n                    inputVertices.stream()\n                            .map(SchemaInfo::getCatalogTables)\n                            .flatMap(Collection::stream)\n                            .collect(Collectors.toList());\n            checkCatalogTableTypesEqual(mergedInputs, PluginType.SINK, configIndex, factoryId);\n            return Collections.singletonList(mergedInputs.get(0));\n        }\n        return inputVertices.get(0).getCatalogTables();\n    }\n\n    /**\n     * Returns true only when the config declares a schema that actually defines columns, mirroring\n     * the non-placeholder branches of {@code TableSchemaDiscoverer}. A bare {@code schema} block","sourceCodeStart":350,"sourceCodeEnd":386,"githubUrl":"https://github.com/apache/seatunnel/blob/cf67b549a7a6c35fa0beb12d83c62892427ea919/seatunnel-core/seatunnel-starter/src/main/java/org/apache/seatunnel/core/starter/seatunnel/command/DryRunConnectValidator.java#L350-L386","documentation":"DryRunConnectValidator.resolveSinkInputTables rejects a sink that receives more than one upstream input when at least one of those inputs is itself a multi-table source (its SchemaInfo holds multiple CatalogTables). Mixing a multi-table source with other inputs would require per-table routing the sink topology does not support in this validation path, so a ConfigCheckException is thrown naming the offending sink.","triggerScenarios":"A dry-run CONNECT validation of a config where a sink declares multiple 'plugin_input' upstreams and one upstream is a source that expanded into multiple catalog tables (multi-table source such as a pattern-matched table list), while other upstreams are single-table sources or transforms.","commonSituations":"Wiring a multi-table source (e.g. MySQL CDC matching many tables) together with additional sources/transforms into one sink; copying DAG wiring from a single-table job into a multi-table job.","solutions":["Give the multi-table source its own dedicated sink, and wire other inputs to a separate sink.","Reduce the multi-table source to a single table so it can be merged with other inputs.","Insert a transform that consolidates schemas if merging is truly intended, ensuring a single upstream for the sink."],"exampleFix":"// before\nsink {\n  Console {\n    plugin_input = [multi_table_source, other_source]\n  }\n}\n// after\nsink {\n  Console { plugin_input = multi_table_source }\n  Console { plugin_input = other_source }\n}","handlingStrategy":"validation","validationCode":"// before validating, ensure a sink with multiple plugin_input values has no multi-table source upstream\nconst multiTableOutputs = sources.filter(s => s.catalogTables.length > 1).map(s => s.id);\nconst badSink = sinks.find(k => inputsOf(k).length > 1 && inputsOf(k).some(i => multiTableOutputs.includes(i)));\nif (badSink) throw new Error(\"Sink \" + badSink.id + \" mixes a multi-table source with other inputs\");","typeGuard":null,"tryCatchPattern":"try { validateConf(conf, DryRun.CONNECT); } catch (ConfigCheckException e) { if (e.getMessage().contains(\"does not support writing both a multi-table source\")) { splitSinkWiring(conf); } else { throw e; } }","preventionTips":["Wire multi-table sources to dedicated sinks","Keep one upstream per sink when using multi-table sources","Review plugin_input lists whenever a source's table pattern may match multiple tables"],"tags":["dry-run","sink","multi-table","dag-validation"],"backgroundTag":"unsupported-operation","analyzedSha":"cf67b549a7a6c35fa0beb12d83c62892427ea919","analyzedAt":"2026-09-10T21:44:55.265Z","contentChangedAt":"2026-09-10T21:44:55.265Z","schemaVersion":2},"datasetVersion":"2026-09-14T11:17:12.474Z"}