apache/seatunnel · error · ConfigCheckException
At least one sink plugin must be configured.
Error message
At least one sink plugin must be configured.
What it means
validateSinks requires the job config to declare at least one sink plugin; an empty sink list throws ConfigCheckException('At least one sink plugin must be configured.'). Every SeaTunnel job must write data somewhere.
Solutions
- Add a sink { ... } block (e.g. Console {}) to the job config
- Ensure 'sink' is a list-shaped block containing a valid plugin name
- Confirm the sink plugin name is spelled correctly so it parses as a plugin config
Example fix
// before
env {...}
source { Fake {} }
// after
env {...}
source { Fake {} }
sink { Console {} } Defensive patterns
Strategy: validation
Validate before calling
boolean hasSink = config.hasPath("sink") && !config.getConfigList("sink").isEmpty(); if (!hasSink) throw new IllegalArgumentException("config must define a sink block"); Try / catch
try { ConfigValidationUtils.validate(config); } catch (ConfigCheckException e) { log.error("Job config invalid: {}", e.getMessage()); } Prevention
- Always include at least Console {} sink in templates
- Lint configs for source+sink presence
- Never rename the reserved 'sink' key
When it happens
Trigger: Job config has no 'sink' block, an empty sink list, or sink entries not recognized as configs by getConfigList (e.g. sink written as object instead of list).
Common situations: Testing source-only configs, deleting the sink section while trimming examples, or a typo turning the sink block into something TypesafeConfigUtils ignores.
Understand the failure class
Background: "is required", "must be set", "missing required field": configuration validation errors across open-source libraries — this error's family across 36 libraries.
Related errors
- GRAPHQL_SINK_PARAMETER_ERROR
- GRAPHQL_SOURCE_PARAMETER_ERROR
- Option 'field_delimiter' cannot be empty
- Option 'field_delimiter' cannot be empty
- Option 'max_in_flight' must be greater than zero
AI-assisted analysis of apache/seatunnel@cf67b549a7 (2026-09-10).
Data as JSON: /api/errors/4ac8fc39558a7bc1.
Report an issue: GitHub.
Appendix: source
Thrown at seatunnel-core/seatunnel-core-starter/src/main/java/org/apache/seatunnel/core/starter/utils/ConfigValidationUtils.java:336
.validate(factory.optionRule());
SeaTunnelTransform<?> transform = factory.createTransform(context).createTransform();
transform.setJobContext(jobContext);
String pluginOutputIdentifier =
ReadonlyConfig.fromConfig(transformConfig).get(PLUGIN_OUTPUT);
outputTables.put(
pluginOutputIdentifier,
new TableInfo(transform.getProducedCatalogTables(), pluginOutputIdentifier));
}
return new ArrayList<>(outputTables.values());
}
private static void validateSinks(
List<TableInfo> upstreamTables,
List<? extends Config> sinkConfigs,
JobContext jobContext) {
if (sinkConfigs.isEmpty()) {
throw new ConfigCheckException("At least one sink plugin must be configured.");
}
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
SeaTunnelFactoryDiscovery factoryDiscovery =
new SeaTunnelFactoryDiscovery(TableSinkFactory.class, ADD_URL_TO_CLASSLOADER);
SeaTunnelSinkPluginDiscovery sinkPluginDiscovery =
new SeaTunnelSinkPluginDiscovery(ADD_URL_TO_CLASSLOADER);
Function<PluginIdentifier, SeaTunnelSink> fallbackCreateSink =
sinkPluginDiscovery::createPluginInstance;
TableInfo defaultInput = upstreamTables.get(upstreamTables.size() - 1);
for (Config sinkConfig : sinkConfigs) {
PluginIdentifier pluginIdentifier = getPluginIdentifier(sinkConfig, PluginType.SINK);
TableSinkFactory sinkFactory =
(TableSinkFactory)
factoryDiscovery
.createOptionalPluginInstance(pluginIdentifier)
.orElse(null);View on GitHub (pinned to cf67b549a7)