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

  1. Always create FileSystemTableSink through the FileSystemTableFactory to ensure format discovery is performed.
  2. If constructing directly, ensure at least one of bulkWriterFormat or serializationFormat is non-null.
  3. 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

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


AI-assisted analysis of apache/flink@2f3c205e92 (2026-08-14). Data as JSON: /api/errors/9a9a57c8d22068c5. Report an issue: GitHub.