TrainException and TrainExceptionData
TrainException is the exception Trax uses for a failure it raises itself, and the type a recorded failure is rebuilt as. TrainExceptionData is the structured record of a junction failure: which train, which run, which junction, what was thrown. It is attached to the exception, stored on the run's metadata, and sent between processes as JSON.
Signatures
namespace Trax.Core.Exceptions;
public class TrainException : Exception
{
public TrainException(string message);
}
public class TrainExceptionData
{
public required string TrainName { get; set; } // "trainName"
public required string TrainExternalId { get; set; } // "trainExternalId"
public required string Type { get; set; } // "type"
public required string Junction { get; set; } // "junction"
public required string Message { get; set; } // "message"
public string? StackTrace { get; set; } // "stackTrace"
public FailureClass? FailureClass { get; set; } // "failureClass"
}
public enum FailureClass { Unclassified = 0, Transient = 1, Conflict = 2, Permanent = 3 }The comments are the JSON property names.
TrainExceptionData fields
| Field | Description |
|---|---|
TrainName | The train class's short name (GetType().Name) for a junction failure. For a failure raised outside any junction, Trax.Effect records the train's canonical name instead. |
TrainExternalId | The failing run's ExternalId, matching the external id on its metadata row |
Type | The thrown exception's type name, for example InvalidOperationException |
Junction | The junction class's short name |
Message | The exception's message |
StackTrace | Where it was thrown; nullable so older serialized records still read |
FailureClass | How the failure was classified where it happened, or null. Carried so a remote run's classification survives the trip home. See IFailureClassifier. |
Where you meet them
- On a caught exception. When a junction throws, its exception is returned unchanged, with the record in
exception.Data["TrainExceptionData"]. The message is not rewritten, so code outside Trax sees the original exception. - On a failed run's metadata. The failure fields on
Metadata(FailureJunction,FailureException,FailureReason,StackTrace,FailureClass) come from this record. - From a remote run. A failure that crossed a process boundary is rebuilt as a
TrainExceptionwhose message is the record's JSON. Only an exception whose type is exactlyTrainExceptionis read this way; a subclass's message is treated as its own text.
Throwing it
Throw TrainException (or your own subclass of it) from a junction for a failure whose message you mean callers to see. Trax.Api's error filter and the scheduler's runner endpoints pass a TrainException's message through as written by the train's author, and do not do that for other exception types.
public override Task<Shipment> Run(Order order) =>
order.Lines.Count == 0
? throw new TrainException($"Order {order.Id} has no lines to ship.")
: shipping.Book(order, CancellationToken);Do not put a secret or another user's data in that message.
See Classifying failures and Metadata for the concepts.
Package
dotnet add package Trax.Core