Error Handling

Learn how to effectively handle errors using Lens tools.


Throughout this documentation, you'll notice a recurring theme: the deliberate distinction between errors as either failures or exceptions.

This page doesn't aim to delve into the historical reasons behind this distinction. Instead, it provides a definition and explains why this distinction is useful for Lens tools.

Failures vs. Exceptions

For any non-trivial task, we can identify three types of possible outcomes:

  • Success: The task was completed successfully according to the intended business logic.

  • Failure: The task failed due to a known failure scenario. All known failure scenarios are usually identified in advance and are strictly related to the business logic of the task.

  • Exception: The task failed due to unexpected circumstances. These are not strictly related to the business logic at hand and are usually caused by a malfunction at a lower level or in the environment the task is running in.

The distinction between Failure and Exception is strictly related to the domain of the problem at hand. It's not possible to draw a clear distinction that works for all types of software, so context-free examples aren't very useful.

Even within the Lens tools themselves (SDKs and API), you'll notice different interpretations due to the different levels of abstraction these tools operate in. Generally speaking, the Lens React SDK allows you to think in terms of Lens Primitives, while the Lens JavaScript SDK and the Lens API expose the underlying building blocks.

Benefits

The Lens SDKs and Lens API adopt this distinction because it:

  • Promotes explicitness: It makes the consumers aware of all possible failure scenarios of a given operation.

  • Enables exhaustive checking: It provides a way for consumers to perform exhaustive error handling and, as the tools evolve, be notified of new failure scenarios.

  • Simplifies reasoning: The consumer's code needs to be explicit in how it handles failures, therefore there are no subtle behaviors that can creep in.


SDK Error Handling

Both the Lens React SDK and the Lens JavaScript SDK use a Result object as the return value for many of their functions.

This object can exist in one of two states:

  • Success: The function executed successfully, and the result is accessible through the value property.

  • Failure: The function failed due to a known reason, and the error describing it is accessible through the error property.

Every SDK function that returns a Result object is informing you about the possible failures that can occur. Exceptions, on the other hand, are thrown and bubbles up the call stack.

One of the advantages of this object is that once its state is determined (either success or failure), the TypeScript compiler can infer the other state.

For instance, if we have const result: Result<number, Error>, your code can be both concise and type-safe:

Example
if (result.isSuccess()) {  // TypeScript knows that result.value exist and it's a number  console.log(result.value.toFixed());  return;}
// because of the eager return above,// TypeScript knows that result.error exist and it's an Errorconsole.log(result.error.message);

To illustrate these concepts we'll create a Post, familiarity with Content Creation is required.

Failures

When you call the execute function, it returns a Result<T, E> object:

Example
const { execute, loading, error } = useCreatePost();
// ...
// within an async callbackconst result = await execute({  metadata: 'ipfs://Qm...',});
if (result.isFailure()) {  window.alert(result.error.message);  return;}
// continue ...

In this context:

  • T represents a PostAsyncResult, which is the success value.

  • E is a TypeScript union of all the possible errors that can occur.

You can use a switch statement to exhaustively handle all possible errors:

Example
if (result.isFailure()) {  switch (result.error.name) {    case 'BroadcastingError':      console.log('There was an error broadcasting the transaction', error.message);      break;
    case 'PendingSigningRequestError':      console.log(        'There is a pending signing request in your wallet. ' +          'Approve it or discard it and try again.'      );      break;
    case 'WalletConnectionError':      console.log('There was an error connecting to your wallet', error.message);      break;
    case 'UserRejectedError':      // the user decided to not sign, usually this is silently ignored by UIs      break;    }  }
  // eager return  return;}
// success ...

Use your IDE's suggestions or refer to the SDK reference documentation to discover all the possible errors of a given Result<T, E> object.

Exceptions

Exceptions originating from calls to the Lens React SDK functions typically relate to connectivity issues or other unexpected malfunctions.

To handle these exceptions, you can:

  • Use a try/catch statement:

    Example
    try {  const result = await execute({    metadata: 'ipfs://Qm...',  });
      // ...} catch (error) {  console.log('There was an unexpected error', error.message);  return;}
  • Leverage a React Error Boundary if the exceptions occur within the React component lifecycle.

  • Use a global error handler if the exceptions occur in event handlers or other asynchronous callbacks.

    Example
    window.addEventListener('unhandledrejection', (event) => {  console.log('There was an unexpected error', event.reason.message);});

You can use any combination of the above methods as needed.

API Error Handling

The Lens API also distinguishes between failures and exceptions.

We'll use the postOnchain mutation to illustrate these concepts. Familiarity with the Content Creation page will be beneficial.

Failures

The API models failure scenarios leveraging Union Types, as described in this insightful article by The Guild.

Mutation
mutation {  postOnchain(request: { contentURI: "<metadata-uri>" }) {    ... on RelaySuccess {      txId      txHash    }    ... on LensProfileManagerRelayError {      reason    }  }}

The response can either be a RelaySuccess or a LensProfileManagerRelayError.

{  "data": {    "postOnchain": {      "txId": "c30869d4-9a3a-4373-b5dc-811ea393bdf5",      "txHash": "0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef"    }  }}

In the case of a LensProfileManagerRelayError, you can use the reason field to determine the exact failure scenario.

Exceptions

At the Lens API level, we consider exceptions to be errors originating from incorrect use of the API or severe malfunctions.

These are returned as GraphQL error responses.

GraphQL error response
{  "errors": [    {      "message": "Cannot query field \"postOnchain\" on type \"Mutation\".",      "locations": [        {          "line": 2,          "column": 3        }      ]    }  ]}

Depending on the GraphQL client you're using, these errors may either be returned as data or encapsulated in a language-specific error object and thrown.


That's it—you now have an operational understanding of how to handle errors with the Lens tools.