pentaho/pentaho-kettle · error · KettleStepException

MQTTProducer.Error.QOS

MQTTProducer.Error.QOS

Error message

MQTTProducer.Error.QOS

What it means

The MQTT Producer step fails to parse the configured QoS (Quality of Service) level because meta.qos is not a valid integer. Paho's MqttMessage.setQos requires an int (0, 1, or 2), so any non-numeric text in the QoS field triggers a NumberFormatException, which is converted into a KettleStepException with the localized 'MQTTProducer.Error.QOS' message including the offending value.

Solutions

  1. Open the MQTT Producer step dialog and set the QoS field to a plain integer (0, 1, or 2).
  2. If the QoS uses a variable (${QOS}), verify the variable is defined (e.g. via kettle.properties or a Set Variables step) and resolves to a numeric value before the transformation runs.
  3. Inspect the saved transformation XML/ktr for the qos property and remove stray whitespace or non-numeric characters.
  4. If QoS is supplied downstream, validate/normalize it before invoking the step (e.g. coerce '2' vs ' 2').

Example fix

// before (step config XML)
<qos>${QOS_LEVEL}</qos>   // variable not set -> parses literal '${QOS_LEVEL}'
// after
<qos>1</qos>  // or ensure QOS_LEVEL is defined in kettle.properties: QOS_LEVEL=1
Defensive patterns

Strategy: validation

Validate before calling

String qos = meta.qos == null ? "" : meta.qos.trim();
int qosLevel;
try {
  qosLevel = Integer.parseInt(qos);
} catch (NumberFormatException e) {
  throw new IllegalArgumentException("QoS must be an integer, got: '" + qos + "'");
}
if (qosLevel < 0 || qosLevel > 2) {
  throw new IllegalArgumentException("QoS must be 0, 1, or 2, got: " + qosLevel);
}

Type guard

boolean isValidQos(String s) {
  if (s == null) return false;
  try { int v = Integer.parseInt(s.trim()); return v >= 0 && v <= 2; }
  catch (NumberFormatException e) { return false; }
}

Try / catch

try {
  trans.execute(null);
} catch (KettleStepException e) {
  if (e.getMessage() != null && e.getMessage().contains("MQTTProducer.Error.QOS")) {
    logError("Invalid MQTT QoS configured; fix the QoS field to 0, 1, or 2");
  } else {
    throw e;
  }
}

Prevention

When it happens

Trigger: In MQTTProducer.processRow, getMessage(row) calls Integer.parseInt(meta.qos); any meta.qos value that is null, empty, or contains non-digit characters (e.g. 'qos1', '1 ', '0.5') throws this error.

Common situations: The QoS field was left blank in the step dialog; the value came from a variable like ${QOS} that was not defined at runtime, so the literal string is parsed; a user typed an invalid value or copied whitespace into the field; a metadata/kettle XML file was hand-edited with a non-numeric QoS.

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


AI-assisted analysis of pentaho/pentaho-kettle@f3058517a1 (2026-09-13). Data as JSON: /api/errors/85d58000a4068f39. Report an issue: GitHub.

Appendix: source

Thrown at plugins/streaming/impls/mqtt/src/main/java/org/pentaho/di/trans/step/mqtt/MQTTProducer.java:158

          .withServerUris( meta.serverUris )
          .withMqttVersion( meta.mqttVersion )
          .withAutomaticReconnect( meta.automaticReconnect )
          .buildAndConnect();
    } catch ( MqttException e ) {
      connectionError.set( true );
      throw new IllegalStateException( e );
    } catch ( IllegalArgumentException iae ) {
      connectionError.set( true );
      throw iae;
    }
  }

  private MqttMessage getMessage( Object[] row ) throws KettleStepException {
    MqttMessage mqttMessage = new MqttMessage();
    try {
      mqttMessage.setQos( Integer.parseInt( meta.qos ) );
    } catch ( NumberFormatException e ) {
      throw new KettleStepException(
        getString( PKG, "MQTTProducer.Error.QOS", meta.qos ) );
    }
    //noinspection ConstantConditions
    mqttMessage.setPayload( getFieldData( row, meta.messageField )
      .map( this::dataAsBytes )
      .orElse( null ) ); //allow nulls to pass through
    return mqttMessage;
  }

  private byte[] dataAsBytes( Object data ) {
    if ( getInputRowMeta().searchValueMeta( meta.messageField ).isBinary() ) {
      return (byte[]) data;
    } else {
      return Objects.toString( data ).getBytes( UTF_8 );
    }
  }

  /**

View on GitHub (pinned to f3058517a1)