{"record":{"id":"6c8776b3ac4ae733","repo":"apache/seatunnel","slug":"binary-custom-filename-requires-a-unique-filename","errorCode":null,"errorMessage":"Binary custom filename requires a unique filename expression when parallel subtasks are used. Please include <DEFAULT_FILE_NAME_EXPRESSION> or ${uuid} in file_name_expression.","messagePattern":"Binary custom filename requires a unique filename expression when parallel subtasks are used\\. Please include <DEFAULT_FILE_NAME_EXPRESSION> or (.+?) in file_name_expression\\.","errorType":"exception","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"seatunnel-connectors-v2/connector-file/connector-file-base/src/main/java/org/apache/seatunnel/connectors/seatunnel/file/sink/BaseFileSinkWriter.java","lineNumber":111,"sourceCode":"                String errorMsg =\n                        String.format(\"Try to process these fileStates %s failed\", fileSinkStates);\n                throw new FileConnectorException(\n                        CommonErrorCodeDeprecated.WRITER_OPERATION_FAILED, errorMsg, e);\n            }\n            writeStrategy.beginTransaction(fileSinkStates.get(0).getCheckpointId() + 1);\n        } else {\n            writeStrategy.beginTransaction(1L);\n        }\n        preCheckConfig(context);\n    }\n\n    private void preCheckConfig(SinkWriter.Context context) {\n        if (writeStrategy.getFileSinkConfig().getFileFormat() == FileFormat.BINARY\n                && writeStrategy.getFileSinkConfig().isCustomFilename()\n                && context.getNumberOfParallelSubtasks() > 1\n                && !hasParallelUniqueFilenameExpression(\n                        writeStrategy.getFileSinkConfig().getFileNameExpression())) {\n            throw new IllegalArgumentException(\n                    \"Binary custom filename requires a unique filename expression when parallel \"\n                            + \"subtasks are used. Please include \"\n                            + DEFAULT_FILE_NAME_EXPRESSION\n                            + \" or ${\"\n                            + Constants.UUID\n                            + \"} in \"\n                            + FILE_NAME_EXPRESSION.key()\n                            + \".\");\n        }\n        if (writeStrategy.getFileSinkConfig().isSingleFileMode()\n                && context.getNumberOfParallelSubtasks() > 1) {\n            if (StringUtils.isNotEmpty(writeStrategy.getFileSinkConfig().getFileNameExpression())\n                    && !writeStrategy\n                            .getFileSinkConfig()\n                            .getFileNameExpression()\n                            .contains(DEFAULT_FILE_NAME_EXPRESSION)) {\n                throw new IllegalArgumentException(\n                        \"Single file mode is not supported when \"","sourceCodeStart":93,"sourceCodeEnd":129,"githubUrl":"https://github.com/apache/seatunnel/blob/cf67b549a7a6c35fa0beb12d83c62892427ea919/seatunnel-connectors-v2/connector-file/connector-file-base/src/main/java/org/apache/seatunnel/connectors/seatunnel/file/sink/BaseFileSinkWriter.java#L93-L129","documentation":"When writing BINARY files with a custom filename in a parallel sink (multiple subtasks), each subtask would generate the same file name and overwrite each other. preCheckConfig guards this: if the custom file_name_expression lacks a per-subtask-unique token (<DEFAULT_FILE_NAME_EXPRESSION> or ${uuid}), it throws IllegalArgumentException.","triggerScenarios":"Configuring a file sink with file_format = binary, a custom file_name_expression without the default-expression or ${uuid} placeholder, while sink parallelism (context.getNumberOfParallelSubtasks()) is greater than 1.","commonSituations":"Copying a single-subtask binary sink config and scaling parallelism up; custom static filenames for images/binaries assuming one writer; forgetting that binary writes do not append transaction/part suffixes the way text formats do.","solutions":["Include ${uuid} in file_name_expression (e.g. file_name_expression = \"${uuid}_photo.jpg\") to make each output unique","Use <DEFAULT_FILE_NAME_EXPRESSION> in the expression so the default unique naming is embedded","Keep sink parallelism at 1 if a fixed filename is truly required"],"exampleFix":"// before\nsink {\n  LocalFile {\n    file_format = \"binary\"\n    file_name_expression = \"photo\"\n    parallelism = 2\n  }\n}\n// after\nsink {\n  LocalFile {\n    file_format = \"binary\"\n    file_name_expression = \"${uuid}_photo\"\n    parallelism = 2\n  }\n}","handlingStrategy":"validation","validationCode":"if (\"binary\".equals(config.get(\"file_format\")) && config.get(\"file_name_expression\") != null\n        && parallelism > 1\n        && !config.get(\"file_name_expression\").contains(\"${uuid}\")\n        && !config.get(\"file_name_expression\").contains(\"<DEFAULT_FILE_NAME_EXPRESSION>\")) {\n    throw new IllegalArgumentException(\"binary custom filename must embed ${uuid} or <DEFAULT_FILE_NAME_EXPRESSION> at parallelism > 1\");\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Always embed ${uuid} in custom binary filenames when parallelism may exceed 1","Keep a fixed filename only for single-subtask binary sinks"],"tags":["sink","binary","filename-collision"],"backgroundTag":"conflicting-config-options","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"}