Skip to content

Failure

Each error caught in a step gives a Failure object, sent to the failures PCollection.

public class Failure implements Serializable {
private final String pipelineStep; // Name of the step
private final String inputElement; // Input element, as a string (toString)
private final Throwable exception; // Exception raised by the step
private final String originElement; // Element that entered the flow, null if not tracked (see Origin element)
private final Instant timestamp; // When the failure was created (since 1.3.0)
public static <T> Failure from(String pipelineStep, WithFailures.ExceptionElement<T> exceptionElement);
public static <T> Failure from(String pipelineStep, T element, Throwable exception);
// Computed from the exception (since 1.3.0), e.g. to write the failure to a dead letter queue.
public String getExceptionType(); // Class name, the original one for a SerializableThrowable
public String getExceptionMessage();
public String getStackTrace(); // Causes included
// Encoded elements with their coder, null if not encoded (since 1.4.0, see withEncodedElements).
public byte[] getInputElementBytes();
public String getInputElementCoder();
public byte[] getOriginElementBytes();
public String getOriginElementCoder();
// Getters...
}

To write the failures to BigQuery or to add them to a Beam ErrorHandler, see Write the failures and Beam native error handling.

If the exception can’t be serialized, it’s replaced by a SerializableThrowable keeping the original class name, message, stack trace and causes (see Failure handling guarantees).

The input element is stored as a string:

  • Java: the toString() of the element.
  • Python: the JSON string of a dict (non JSON types like datetime or bytes converted with str), the str() of any other element.

To control the format, give a function to the composer. It applies to all the next steps, and it’s the way to go for the classes you don’t own (Avro, Protobuf, third party classes): e.g. JSON, a format masking sensitive data, or an identifier.

CollectionComposer.of(orders)
.withInputElementToString(element -> JsonUtil.serialize(element)) // since 1.4.0
.apply("Parse", ...)
.apply("Validate", ...)
.getResult();

The function is evaluated only when a failure occurs. It never breaks the job: if it fails or returns null (None), the element is converted with the default conversion.

You can also implement the string representation of your own objects:

public class Team implements Serializable {
// Fields...
@Override
public String toString() {
// Jackson is already a dependency of Beam.
return JsonUtil.serialize(this);
}
}

The string is enough to read, search and debug, not to replay an element exactly (republish the Pub/Sub or Kafka message as it entered, binary formats like Avro or Protobuf). With withEncodedElements, the failures also keep the input element, and the origin element, encoded with their coder:

final WithFailures.Result<PCollection<Order>, Failure> result = CollectionComposer.of(messages)
.withEncodedElements() // since 1.4.0
.apply("Parse", MapElementFn.into(TypeDescriptor.of(Order.class)).via(OrderParser::parse))
.getResult();
// Replay: decode the element with its coder.
final Message message = CoderUtils.decodeFromByteArray(messageCoder, failure.getInputElementBytes());
  • The coder of the step: the element is encoded with the coder of the PCollection consumed by the step, the one the pipeline already uses for these elements. It works for every type Beam can carry: Avro, Protobuf, Row, KV, custom coders. The coder is also kept as a string (getInputElementCoder()).
  • Only when a failure occurs, and it never breaks the job: if the element can’t be encoded, the failure has no bytes.
  • Opt-in: the bytes make the dead letter queue bigger and keep the element in clear.