{"record":{"id":"2d80a6b3e793c13f","repo":"apache/seatunnel","slug":"env-s-mode-route-requires-env-s-sink-plugin-name","errorCode":null,"errorMessage":"env.%s.mode=ROUTE requires env.%s.sink.plugin_name to be configured.","messagePattern":"env\\.(.+?)\\.mode=ROUTE requires env\\.(.+?)\\.sink\\.plugin_name to be configured\\.","errorType":"validation","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"seatunnel-engine/seatunnel-engine-server/src/main/java/org/apache/seatunnel/engine/server/task/error/ErrorHandlerConfigUtil.java","lineNumber":107,"sourceCode":"        boolean includeStacktrace = getBoolean(stage, global, \"include_stacktrace\", false);\n        boolean includeOriginalData = getBoolean(stage, global, \"include_original_data\", false);\n\n        String dataFormatStr = getString(stage, global, \"original_data_format\", \"TEXT\");\n        if (!\"TEXT\".equalsIgnoreCase(dataFormatStr)) {\n            throw new IllegalArgumentException(\n                    \"Unsupported original_data_format='\"\n                            + dataFormatStr\n                            + \"'. Current version only supports TEXT.\");\n        }\n        OriginalDataFormat originalDataFormat = OriginalDataFormat.TEXT;\n\n        int originalDataMaxLength =\n                getNonNegativeInt(stage, global, \"original_data_max_length\", 8192);\n\n        ErrorSinkConfig sinkConfig = buildErrorSinkConfig(stage, global);\n\n        if (mode == ErrorHandlerMode.ROUTE && (sinkConfig == null || !sinkConfig.isConfigured())) {\n            throw new IllegalArgumentException(\n                    String.format(\n                            \"env.%s.mode=ROUTE requires env.%s.sink.plugin_name to be configured.\",\n                            stageKey, stageKey));\n        }\n\n        return StageErrorConfig.builder()\n                .mode(mode)\n                .sink(sinkConfig)\n                .maxErrorRatio(maxErrorRatio)\n                .maxErrorRatioMinRecords(maxErrorRatioMinRecords)\n                .maxErrorRecords(maxErrorRecords)\n                .queueCapacity(queueCapacity)\n                .queueOverflowPolicy(overflowPolicy)\n                .includeStacktrace(includeStacktrace)\n                .includeOriginalData(includeOriginalData)\n                .originalDataFormat(originalDataFormat)\n                .originalDataMaxLength(originalDataMaxLength)\n                .build();","sourceCodeStart":89,"sourceCodeEnd":125,"githubUrl":"https://github.com/apache/seatunnel/blob/cf67b549a7a6c35fa0beb12d83c62892427ea919/seatunnel-engine/seatunnel-engine-server/src/main/java/org/apache/seatunnel/engine/server/task/error/ErrorHandlerConfigUtil.java#L89-L125","documentation":"IllegalArgumentException from ErrorHandlerConfigUtil.buildStageConfig when a stage's error-handler mode is ROUTE but no error-sink writer is configured (sinkConfig null or not configured). ROUTE mode must know where to send bad rows, so the job is rejected at config validation.","triggerScenarios":"Setting env.<stage>.mode = ROUTE (or global mode=ROUTE) without env.<stage>.sink.plugin_name (or a sufficiently complete sink block).","commonSituations":"Copy-pasting a stage config and forgetting the sink sub-block; specifying sink options like plugin_type but omitting plugin_name; global DISABLE config edited to ROUTE without adding a sink.","solutions":["Add sink { plugin_name = \"...\" } under the stage error-handler block (e.g. Console, JDBC, File).","Verify the exact env key prefix matches the stage key shown in the error message.","Switch mode to FAIL or IGNORE if you don't actually need to route bad rows."],"exampleFix":"// before\nerror-handler { mode = ROUTE }\n// after\nerror-handler { mode = ROUTE\n  sink { plugin_name = \"Console\" } }","handlingStrategy":"validation","validationCode":"if (\"ROUTE\".equalsIgnoreCase(mode) && !config.hasPath(\"sink.plugin_name\")) {\n  throw new IllegalArgumentException(\"mode=ROUTE requires error-handler sink.plugin_name\");\n}","typeGuard":null,"tryCatchPattern":"try { buildStageConfig(stage, global); } catch (IllegalArgumentException e) { if (e.getMessage().contains(\"ROUTE requires\")) { addSinkPluginName(); } }","preventionTips":["Always pair mode=ROUTE with a sink { plugin_name = ... } block","Recheck stage configs after copy-pasting between stages","Run config validation in CI for SeaTunnel job files"],"tags":["config","missing-required-config","route-mode"],"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"}