Apibara blog

News and updates about Apibara

Improved error messages in the CLI

This week, we worked on improving the developer experience using Apibara from the command line. The Apibara CLI and Sinks' latest release comes with improved error messages.

For example, trying to execute the apibara run command with non-existing files results in the following error message. This message provides enough context to help you debug the error. If you feel stuck, you can always post the error in our Discord, and we will do our best to help you.

$ apibara run path/to/indexer.ts
# Error: cli operation failed
# ├╴at cli/src/run.rs:36:64
#
# ╰─▶ failed to load script
#     ├╴at sinks/sink-common/src/cli.rs:34:14
#     ╰╴script file not found: path/to/indexer.ts

The CLI will warn you if the indexer doesn't export the config or transform function or if any key was misspelt.

$ apibara run indexer.js
# sink configuration error
# ├╴at /tmp/nix-build-apibara-0.0.0.drv-0/source/sinks/sink-common/src/lib.rs:75:10
# ├╴invalid sink options
#
# ╰─▶ webhook sink operation failed
#     ├╴at sinks/sink-webhook/src/configuration.rs:49:14
#     ╰╴missing target url

One challenge when running Apibara integrations in production is figuring out if the indexer exited because of a configuration error or because of a transient error. This is important to decide whether to restart the indexer or not. This release helps operators by returning a different Unix exit code based on the error type. These follow the codes defined in the sysexit.h header.

  • 0: the indexer was interrupted and exited successfully.
  • 78: configuration error. The indexer should not be restarted.
  • 75: temporary error. The indexer should be restarted after a back-off period.
  • All other exit codes: fatal error. The indexer should not be restarted.

The error-stack crate

We improved error handling by changing how we manage errors in the Apibara source code. Before this release, Apibara implemented errors following Rust best practices with libraries exporting one or more error types implemented using thiserror and applications using eyre. This approach worked well initially, but thiserror encourages reusing the same variant for errors of the same type (e.g. MyError::Io(std::io::Error) for io errors). Any context of what operation caused the error is lost, resulting in error messages not helping users fix their bugs.

Error-stack approach is a hybrid between thiserror and eyre (or anyhow). Like thiserror, libraries and applications should define their error type. This can be anything. In Apibara, we use both structs and enums. Like eyre, it's possible to attach additional context to errors to provide more information to end users.

pub fn load_script(path: &str, options: ScriptOptions) -> Result<Script, LoadScriptError> {
    let Ok(_) = fs::metadata(path) else {
        return Err(LoadScriptError)
            .attach_printable_lazy(||
              format!("script file not found: {path:?}")
            );
    };

    let current_dir = std::env::current_dir()
        .change_context(LoadScriptError)
        .attach_printable("failed to get current directory")?;

    let script = Script::from_file(path, current_dir, options)
        .change_context(LoadScriptError)
        .attach_printable_lazy(||
          format!("failed to load script at path: {path:?}")
        )?;

    Ok(script)
}

This change is only the first step in a better developer experience. You should expect more improvements in the following weeks and months.

Apibara

Apibara is the fastest platform to build production-grade indexers that connect onchain data to web2 services.

© 2024 GNC Labs Limited. All rights reserved.