Chapter 5 - Error Handling¶
This tutorial is based on the error_handling.js example, which can be found in the TRAC GitHub Repository under examples/apps/javascript.
Error handling basics¶
The TRAC web API bindings support both futures and callback-style API calls. The same error handling capabilities are available in both styles.
As a basic example, we can send an invalid request to the server and it will respond with a validation error. Here is what that looks like using futures:
63 // Handle an error in the API call using futures
64 return metaApi.createObject(badRequest)
65 .then(_ => {}) // handle response
66 .catch(error => {
67 // handle error
68 console.log("There was a problem creating the object: " + error.message);
69 console.log("gRPC status code: " + error.code);
70 });
The error.message property contains a short human-readable description of the error. You can also access the gRPC status code using the error.code property. Both of these properties are available even if communication with the server fails, for example in the case of network errors.
Note
A list of gRPC status codes is available in public documentation for gRPC: https://grpc.io/docs/guides/status-codes/
Exactly the same error handling capability is available using callbacks:
79 // Handle the same error using callback-style API calls
80 metaApi.createObject(badRequest, (error, _) => {
81 if (error != null) {
82 // handle error
83 console.log("There was a problem creating the object: " + error.message);
84 console.log("gRPC status code: " + error.code);
85 } else {
86 // handle response
87 }
88 });
TRAC Error Details¶
Sometimes it is helpful to get more detailed information about an error. For example, in the case of validation failures, there can sometimes be multiple issues and we want to know exactly what those issues are and where in the input they occurred. Fortunately, TRAC has a means to provide this information.
97 // Handle an error in the API call using futures
98 return metaApi.createObject(badRequest)
99 .then(_ => {}) // handle response
100 .catch(error => {
101 // Get structured error details
102 const details = tracdap.utils.getErrorDetails(error);
103 console.log("There was a problem creating the object: " + details.message);
104 console.log("gRPC status code: " + details.code);
Use getErrorDetails() to get a TracErrorDetails
object. The details.message and details.code properties are the same as for basic
error handling and are always available. However, if the server provided more detailed
information, this can be accessed by looking at the individual error items:
105 // Show more detailed information
106 details.items.forEach(item => {
107 console.log(item.fieldPath + ": " + item.detail);
108 });
For each item, item.detail is a human readable description of the error. For errors related to the request (mostly validation errors), item.fieldPath is the field path for the input field that caused the problem.
Errors in streaming calls¶
See also
See Chapter 4 - Streaming for more details on the streaming data API.
Errors in upload streams
With upload streams, the client sends a series of messages to the server and receives a single message or error in response. The error handler can be attached when you send the first message to the stream:
stream.createDataset(request0)
.then(_ => {...}) // Handle upload success
.catch(error => {
// Handle upload error
const details = tracdap.utils.getErrorDetails(error);
...
});
// Now send the rest of the messages to the stream
stream.createDataset(msg1);
stream.createDataset(msg2);
...
Similarly if you are using callback style, a callback is only needed for the first message of the stream.
Errors in download streams
For download streams, the client sends a single request to the server and receives a stream of messages in response. An error can occur at any point in the stream and has to be handled using a stream event handler.
Because errors are processed using stream events, the futures / callback handlers should be explicitly set to no-op functions to avoid unhandled or duplicate events.
// Handle the download stream
stream.on("data", msg => {...});
stream.on("end", () => {...});
stream.on("error", error => {
// Handle download error
const details = tracdap.utils.getErrorDetails(error);
...
});
// Disable future / callback processing, because we are using stream events
stream.readDataset(request)
.then(_ => {})
.catch(_ => {});
Using promises for streaming operations
In Chapter 4 - Streaming, the streaming operations are wrapped up into promises and errors are
passed directly to the promise resolve() method. Once the operation is wrapped up into a promise,
errors can be processed using a regular .catch().
For example, the streaming download API readDataset() is
wrapped up into a promise by loadStreamingData() and handled as shown in this example, hiding the
details of the stream event processing:
145 return loadStreamingData(dataId)
146 .then(_ => {}) // Handle success
147 .catch(error => {
148 const details = tracdap.utils.getErrorDetails(error);
149 console.log("Download failed: " + details.message);
150 details.items.forEach(item => console.log(item.detail));
151 });