Error Handling with Async/Await

Introduction

When working with asynchronous operations, errors can occur due to reasons such as failed API requests, missing files, database connection issues, network problems, or invalid user input. Properly handling these errors is essential for building reliable and maintainable applications.

With Async/Await, JavaScript provides a simple and readable way to handle errors using the try...catch statement. Instead of attaching .catch() to every Promise, developers can wrap asynchronous code inside a try block and handle any errors in the corresponding catch block.

In Node.js, try...catch is commonly used with await for file operations, API calls, database queries, browser automation, and cloud services.

For automation engineers, proper error handling ensures that test failures are reported clearly, resources are cleaned up properly, and automation scripts continue to behave predictably.

In this tutorial, you’ll learn how to handle errors effectively when using Async/Await.


Why is Error Handling Important?

Error handling helps developers:

  • Prevent application crashes.

  • Display meaningful error messages.

  • Debug problems more easily.

  • Improve application reliability.

  • Handle unexpected failures gracefully.

  • Write maintainable code.

  • Improve automation stability.


Using try...catch

The most common way to handle errors with Async/Await is by using the try...catch statement.

If the awaited Promise is rejected, execution immediately moves to the catch block.


Syntax

async function example() {

    try {

        const result =

            await promise;

        console.log(result);

    }

    catch (error) {

        console.log(error);

    }

}

Example 1: Basic Error Handling

async function test() {

    try {

        const result =

            await Promise.reject(

                "Something went wrong."

            );

        console.log(result);

    }

    catch (error) {

        console.log(error);

    }

}

test();

Sample Output

Something went wrong.

Example 2: Using an Error Object

async function login() {

    try {

        await Promise.reject(

            new Error(

                "Invalid username."

            )

        );

    }

    catch (error) {

        console.log(error.message);

    }

}

login();

Sample Output

Invalid username.

Example 3: Reading a File

const fs =
    require("fs").promises;

async function readFile() {

    try {

        const data =

            await fs.readFile(

                "sample.txt",

                "utf8"

            );

        console.log(data);

    }

    catch (error) {

        console.log(error.message);

    }

}

readFile();

If the file does not exist, the error is handled inside the catch block instead of crashing the application.


Example 4: API Request Simulation

async function fetchData() {

    try {

        await Promise.reject(

            "API request failed."

        );

    }

    catch (error) {

        console.log(error);

    }

}

fetchData();

Sample Output

API request failed.

Example 5: Using finally

The finally block always executes, whether an error occurs or not.

async function processTask() {

    try {

        console.log(

            await Promise.resolve(

                "Processing..."

            )

        );

    }

    catch (error) {

        console.log(error);

    }

    finally {

        console.log(

            "Task finished."

        );

    }

}

processTask();

Sample Output

Processing...
Task finished.

Automation Testing Examples

Error handling is essential for reliable automation scripts.

Playwright Example

Handle a page loading failure.

async function openApplication() {

    try {

        await Promise.reject(

            "Application failed to load."

        );

    }

    catch (error) {

        console.log(error);

    }

}

openApplication();

Sample Output

Application failed to load.

Selenium Example

Handle a browser launch failure.

async function launchBrowser() {

    try {

        await Promise.reject(

            "Browser launch failed."

        );

    }

    catch (error) {

        console.log(error);

    }

}

launchBrowser();

Sample Output

Browser launch failed.

Cypress Example

Handle a missing element.

async function verifyElement() {

    try {

        await Promise.reject(

            "Element not found."

        );

    }

    catch (error) {

        console.log(error);

    }

}

verifyElement();

Sample Output

Element not found.

API Testing Example

Handle an API failure.

async function callApi() {

    try {

        await Promise.reject(

            "API request failed."

        );

    }

    catch (error) {

        console.log(error);

    }

}

callApi();

Sample Output

API request failed.

Data-Driven Testing Example

Handle a missing CSV file.

async function loadCsv() {

    try {

        await Promise.reject(

            "CSV file missing."

        );

    }

    catch (error) {

        console.log(error);

    }

}

loadCsv();

Sample Output

CSV file missing.

Common Uses of Error Handling

Error handling with Async/Await is commonly used for:

  • File operations.

  • API requests.

  • Database queries.

  • Browser automation.

  • Authentication.

  • Form validation.

  • JSON processing.

  • Data loading.

  • Network communication.

  • Workflow automation.


Common Mistakes

Forgetting try...catch

Unhandled rejected Promises can terminate your application or produce unhandled rejection warnings.


Catching Errors but Ignoring Them

Always log, rethrow, or appropriately handle errors instead of silently ignoring them.


Using await Outside an async Function

The await keyword must be used inside an async function (except supported top-level await in ES modules).


Best Practices

  • Wrap awaited operations in try...catch.

  • Use meaningful error messages.

  • Prefer Error objects instead of plain strings.

  • Use finally for cleanup operations.

  • Keep try blocks focused on the code that might fail.

  • Log errors for debugging.

  • Handle expected and unexpected errors gracefully.


Conclusion

Error handling is a critical part of asynchronous programming. By combining async, await, and try...catch, developers can write clean, readable, and reliable code while handling failures in a structured way.

For automation engineers, proper error handling improves script reliability by managing browser failures, API errors, missing files, timeout exceptions, and validation failures. Mastering error handling with Async/Await is essential for developing robust Node.js applications and professional automation frameworks.


Frequently Asked Questions (FAQs)

How do I handle errors with Async/Await?

Use the try...catch statement to catch rejected Promises and exceptions.


Why use try...catch instead of .catch()?

try...catch provides a cleaner, more readable way to handle errors when using await.


What is the purpose of the finally block?

The finally block always executes, whether the operation succeeds or fails. It is useful for cleanup tasks.


Should I throw Error objects or strings?

It is recommended to throw Error objects because they provide additional debugging information such as stack traces.


Why is error handling important in automation testing?

Automation scripts interact with browsers, APIs, files, and databases, all of which can fail. Proper error handling ensures failures are reported clearly and resources are managed correctly.


Key Takeaways

  • Use try...catch to handle errors with Async/Await.

  • Awaited rejected Promises throw exceptions.

  • catch receives the error for processing.

  • Use finally for cleanup operations.

  • Prefer Error objects over plain strings.

  • Keep try blocks focused on risky operations.

  • Log or handle errors appropriately.

  • Error handling improves application reliability.

  • Automation frameworks rely heavily on try...catch for robust execution.

  • Mastering Async/Await error handling is essential for writing professional Node.js applications and automation scripts.