apache/seatunnel · error · IllegalArgumentException
MQTT QoS must be 0 (at-most-once) or 1 (at-least-once), got:
Error message
MQTT QoS must be 0 (at-most-once) or 1 (at-least-once), got:
What it means
The MQTT sink writer validates the configured qos option in its constructor and rejects values other than 0 or 1 with an IllegalArgumentException. The sink deliberately forbids QoS 2 (exactly-once) and negative values because its publish/ack handling only supports at-most-once and at-least-once delivery.
Source
Thrown at seatunnel-connectors-v2/connector-mqtt/src/main/java/org/apache/seatunnel/connectors/seatunnel/mqtt/sink/MqttSinkWriter.java:69
public class MqttSinkWriter implements SinkWriter<SeaTunnelRow, Void, Void>, MqttCallback {
private static final String CLIENT_ID_PREFIX = "seatunnel_mqtt_sink_task_";
private static final long RETRY_BACKOFF_MS = 200L;
private final String topic;
private final int qos;
private final int retryTimeoutMs;
private final int batchSize;
private final SerializationSchema serializationSchema;
private final List<MqttMessage> messageBuffer;
private MqttClient mqttClient;
public MqttSinkWriter(
SinkWriter.Context context, SeaTunnelRowType rowType, ReadonlyConfig pluginConfig) {
this.topic = pluginConfig.get(MqttSinkOptions.TOPIC);
this.qos = pluginConfig.get(MqttSinkOptions.QOS);
if (this.qos < 0 || this.qos > 1) {
throw new IllegalArgumentException(
"MQTT QoS must be 0 (at-most-once) or 1 (at-least-once), got: " + this.qos);
}
this.retryTimeoutMs = pluginConfig.get(MqttSinkOptions.RETRY_TIMEOUT);
this.batchSize = pluginConfig.get(MqttSinkOptions.BATCH_SIZE);
if (this.batchSize < 1) {
throw new IllegalArgumentException("batch_size must be >= 1, got: " + this.batchSize);
}
this.messageBuffer = new ArrayList<>(this.batchSize);
this.serializationSchema = createSerializationSchema(rowType, pluginConfig);
// Each subtask appends its index and a random UUID to guarantee a globally unique client
// ID,
// preventing mutual disconnections and connection hijacking when running parallel jobs.
String clientId =
CLIENT_ID_PREFIX
+ context.getIndexOfSubtask()
+ "-"
+ java.util.UUID.randomUUID().toString();View on GitHub (pinned to cf67b549a7)
Solutions
- Set qos = 0 for at-most-once delivery.
- Set qos = 1 for at-least-once delivery (recommended for sinks).
- Remove the qos option to use the default instead of an unsupported value.
- If exactly-once is required, MQTT QoS 2 is not supported by this sink — deduplicate downstream.
Example fix
// before
MqttSink {
qos = 2
}
// after
MqttSink {
qos = 1
} Defensive patterns
Strategy: validation
Validate before calling
int qos = config.getInt("qos", 1);
if (qos < 0 || qos > 1) {
throw new IllegalArgumentException("qos must be 0 or 1, got: " + qos);
} Try / catch
try {
new MqttSinkWriter(context, rowType, pluginConfig);
} catch (IllegalArgumentException e) {
if (e.getMessage().startsWith("MQTT QoS must be")) {
// fall back to qos = 1 or surface config error to user
}
throw e;
} Prevention
- Only set qos to 0 or 1 in MQTT sink configs.
- Document that QoS 2 is unsupported by this sink.
- Validate the whole sink config at job-design time.
When it happens
Trigger: Setting `qos = 2`, `qos = -1`, or any value outside [0,1] in the MQTT sink configuration; MqttSinkWriter constructor reads MqttSinkOptions.QOS and fails validation.
Common situations: Users copying QoS 2 settings from other MQTT tools; assuming the sink supports exactly-once; typos producing out-of-range numbers.
Understand the failure class
Background: "Invalid value" and "allowed values are" config errors: what your library rejected and how to fix it — this error's family across 41 libraries.
Related errors
- batch_size must be >= 1, got:
- MQTT source qos must be 0 or 1, got: ${qos}
- The SQL config must contain at least one source table
- The SQL config must contain `INSERT INTO ... SELECT ...` syn
- Table name duplicate: %s
AI-assisted analysis of apache/seatunnel@cf67b549a7 (2026-09-10).
Data as JSON: /api/errors/4c284b4f043b45fd.
Report an issue: GitHub.