Error Handling¶
Data streams can be interrupted, server requests can fail, and unexpected issues can arise. This guide outlines common error scenarios and best practices for handling them.
Types of Errors¶
Errors can occur at different stages of interaction with the Hyperion Stream Client:
- Client Instantiation Errors: Rare, but could occur if invalid options are somehow passed (though TypeScript helps prevent this).
- Initial Connection Errors: When
client.connect()
is called. - Stream Request Errors: When
client.streamActions()
orclient.streamDeltas()
is called. - Ongoing Connection Errors: Disconnections or errors after a connection was established.
- In-Stream Errors: Errors specific to an active data stream after it has started.
- AsyncIterator Errors: Errors encountered while consuming data via the
for await...of
loop.
1. Client Instantiation¶
Usually, if you provide a syntactically correct options object (especially with TypeScript), instantiation itself won't throw. The primary concern here is ensuring the endpoint
is a valid string.
try {
const client = new HyperionStreamClient({
// endpoint: undefined, // This would likely be caught by TypeScript or cause issues later
endpoint: "https://eos.hyperion.eosrio.io"
});
} catch (e) {
// Highly unlikely to catch here with proper options
console.error("Client instantiation failed:", e);
}
2. Initial Connection Errors (client.connect()
)¶
The client.connect()
method returns a Promise
. If the initial connection to the Hyperion server fails (e.g., server down, incorrect endpoint, network issue, CORS issue in browsers), the promise will reject.
Additionally, the client itself will emit an 'error'
event.
// Assuming 'client' is an instance of HyperionStreamClient
client.on("error", (error) => {
// This handler catches various client-level errors, including initial connection failures
// if the connect() promise isn't explicitly caught, or for ongoing issues.
console.error("Client-level error:", error);
});
async function connectToServer() {
try {
console.log("Attempting to connect...");
await client.connect();
console.log("Successfully connected!");
} catch (error) {
console.error("Failed to connect (Promise rejected):", error.message);
// Handle specific error types if needed (e.g., check error.name or error.message)
// Example: if (error.message.includes('timeout')) { ... }
}
}
connectToServer();
- Invalid Hyperion endpoint URL.
- Hyperion server is offline or unreachable.
- Network connectivity problems (client-side or server-side).
- Browser Specific: CORS (Cross-Origin Resource Sharing) issues. The Hyperion server must allow requests from your web application's origin. Check the browser console for CORS errors.
- Connection timeout (see
connectionTimeout
in Client Configuration).
3. Stream Request Errors (streamActions()
& streamDeltas()
)¶
When you call client.streamActions(request)
or client.streamDeltas(request)
, these methods also return Promises. The promise will reject if the Hyperion server cannot fulfill the stream request.
async function requestMyStream() {
if (!client.online) {
console.error("Cannot request stream: Client is not connected.");
return;
}
try {
const stream = await client.streamActions({
contract: "invalid.contract.name", // Example of a potentially invalid parameter
action: "someaction",
start_from: 0
});
// ... set up stream message handlers ...
} catch (error) {
console.error("Failed to initiate stream request:", error.message);
// 'error' might be an object from the server indicating the problem
// e.g., { status: "ERROR", error: "Invalid contract name" }
// or a client-side error if the request couldn't even be made.
if (error.error) { // Check if it's a structured error from Hyperion
console.error("Server error details:", error.error);
}
}
}
- Invalid request parameters (e.g., non-existent contract, malformed filters).
- Server-side issues preventing stream setup for the given parameters.
- The client is not connected when the request is made.
4. Ongoing Connection Errors & Disconnections¶
The Hyperion Stream Client uses socket.io-client
under the hood, which handles automatic reconnections by default.
-
client.on('disconnect', (reason) => { ... })
: This event is fired when the client loses its connection to the server. Thereason
string often indicates why (e.g.,"io server disconnect"
,"transport close"
).client.on("disconnect", (reason) => { console.warn("Client disconnected. Reason:", reason); // Update UI to show disconnected status. // The client will attempt to reconnect automatically. });
-
client.on('connect', () => { ... })
: This event is fired not only on the initial successful connection but also on successful reconnections.client.on("connect", () => { console.log("Client connected (or reconnected)!"); // If you have streams with `replayOnReconnect: true`, they will attempt to restart. // You might need to re-request streams that don't have `replayOnReconnect: true`. });
-
client.on('error', (error) => { ... })
: This client-level error handler can also catch errors related to reconnection attempts or other general socket issues.
Reconnection Behavior:
- Streams configured with
replayOnReconnect: true
in their request options will automatically attempt to re-establish and resume from where they left off (approximately) upon client reconnection. - For streams with
replayOnReconnect: false
, you would need to manually re-request the missed data after aconnect
event, otherwise it will resume from the current live data.
5. In-Stream Errors¶
Once a stream is successfully started, it can still encounter errors specific to that stream. Each stream object returned by streamActions
or streamDeltas
emits its own 'error'
event.
// ... inside a function where 'stream' is an active HyperionStream object ...
stream.on("error", (streamError) => {
console.error(`Error on stream ${stream.reqUUID}:`, streamError);
// This error might indicate the server terminated this specific stream.
// The stream might no longer be usable.
// Consider logging, alerting, or attempting to set up a new stream.
});
- The server decides to terminate a specific stream for some reason (e.g., resource limits, internal server error related to the query).
- Malformed messages from the server (less common).
6. AsyncIterator Errors¶
If you are consuming a stream using the for await...of
loop, errors can occur during iteration. These should be caught using a try...catch
block around the loop.
async function processWithIterator() {
let stream; // Keep stream reference outside try block for potential cleanup
try {
stream = await client.streamActions({ /* ... */ });
for await (const message of stream) {
if (message === null) {
console.log("Stream ended gracefully.");
break;
}
// Process message
if (message.content.block_num % 1000 === 0) { // Artificial error condition
throw new Error("Simulated processing error during iteration.");
}
}
} catch (error) {
console.error("Error during AsyncIterator processing:", error.message);
// The 'stream' might be in an indeterminate state here.
// It's often best to assume the iteration is compromised.
// If 'stream' is defined, you might still try stream.stop() if appropriate,
// though the connection or stream itself might already be closed.
} finally {
console.log("AsyncIterator loop finished or errored out.");
// Perform any cleanup
}
}
for await...of
loop (either by your processing logic or an underlying issue with the stream yielding an error) will be caught by the catch
block.
Best Practices¶
- Always Attach Error Handlers: Attach
'error'
handlers to the client instance and to every stream instance you create. - Catch Promise Rejections: Use
.catch()
ortry...catch
withasync/await
forclient.connect()
,client.streamActions()
, andclient.streamDeltas()
. - Monitor Connection State: Use
client.on('connect', ...)
andclient.on('disconnect', ...)
to understand the current connection status and react accordingly (e.g., update UI, manage stream re-requests). replayOnReconnect: true
: For streams where historical data matters, setreplayOnReconnect: true
in the stream request options to allow the client to attempt to catch missed data after a disconnection.- Logging: Implement comprehensive logging, especially for errors, to help diagnose issues in development and production.
- Retry Strategies: For critical applications, consider implementing custom retry strategies for
client.connect()
or for re-requesting streams if they fail, potentially with exponential backoff. - User Feedback: In UI applications, provide clear feedback to the user about connection status and any errors encountered.
- Test Error Conditions: During development, try to simulate error conditions (e.g., stop your Hyperion server, disconnect network) to test how your application responds.
By thoughtfully handling these various error scenarios, you can build more resilient and reliable applications using the Hyperion Stream Client.
Next Steps¶
- Client Configuration: Review client settings in Client Configuration.
- Browser Usage: Specific considerations for Browser Usage.