apache/flink · error · TableException
Can not find format factory.
Error message
Can not find format factory.
What it means
FileSystemTableSink.createWriter selects the encoding format for writing. If both bulkWriterFormat and serializationFormat are null (no format was discovered), it throws a TableException 'Can not find format factory.' Unlike the constructor validation (error 193) which throws ValidationException, this is a runtime check reached during getSinkRuntimeProvider when the writer is being created.
Source
Thrown at flink-connectors/flink-connector-files/src/main/java/org/apache/flink/connector/file/table/FileSystemTableSink.java:397
return writer instanceof Encoder
? path -> createEncoderOutputFormat((Encoder<RowData>) writer, path)
: path -> createBulkWriterOutputFormat((BulkWriter.Factory<RowData>) writer, path);
}
private Object createWriter(Context sinkContext) {
DataType physicalDataTypeWithoutPartitionColumns =
DataType.getFields(physicalRowDataType).stream()
.filter(field -> !partitionKeys.contains(field.getName()))
.collect(Collectors.collectingAndThen(Collectors.toList(), DataTypes::ROW));
if (bulkWriterFormat != null) {
return bulkWriterFormat.createRuntimeEncoder(
sinkContext, physicalDataTypeWithoutPartitionColumns);
} else if (serializationFormat != null) {
return new SerializationSchemaAdapter(
serializationFormat.createRuntimeEncoder(
sinkContext, physicalDataTypeWithoutPartitionColumns));
} else {
throw new TableException("Can not find format factory.");
}
}
private void checkConfiguredParallelismAllowed(ChangelogMode requestChangelogMode) {
final Integer parallelism = this.configuredParallelism;
if (parallelism == null) {
return;
}
if (!requestChangelogMode.containsOnly(RowKind.INSERT)) {
throw new ValidationException(
String.format(
"Currently, filesystem sink doesn't support setting parallelism (%d) by '%s' "
+ "when the input stream is not INSERT only. The row kinds of input stream are [%s]",
parallelism,
FileSystemConnectorOptions.SINK_PARALLELISM.key(),
requestChangelogMode.getContainedKinds().stream()
.map(RowKind::shortString)
.collect(Collectors.joining(","))));View on GitHub (pinned to 2f3c205e92)
Solutions
- Always create FileSystemTableSink through the FileSystemTableFactory to ensure format discovery is performed.
- If constructing directly, ensure at least one of bulkWriterFormat or serializationFormat is non-null.
- Check copy() implementations to ensure format references are preserved.
Defensive patterns
Strategy: validation
Validate before calling
// Validate formats before constructing the sink
if (bulkWriterFormat == null && serializationFormat == null) {
throw new TableException("At least one writer format must be provided");
} Prevention
- Always create FileSystemTableSink through FileSystemTableFactory.
- If constructing directly, provide a non-null format argument.
- Ensure copy() implementations preserve all format fields.
- Add unit tests that verify format fields survive copy().
When it happens
Trigger: createWriter is called when both bulkWriterFormat and serializationFormat fields are null. This should not happen if the constructor validation (error 193) passed, but can occur if the sink was constructed directly (bypassing the factory) or if the formats were nulled after construction (e.g. via copy()).
Common situations: Direct construction of FileSystemTableSink without providing a format. A bug in copy() that drops the format references. Factory discovery logic changed in a way that passes null formats to the constructor without triggering the constructor check.
Related errors
- Could not find any format factory for identifier '%s' in the
- Exception in finalizeGlobal
- Exception in open
- Exception in writeRecord
- Exception in close
AI-assisted analysis of apache/flink@2f3c205e92 (2026-08-14).
Data as JSON: /api/errors/9a9a57c8d22068c5.
Report an issue: GitHub.