pw_status#

Exception-free error propagation for embedded

Stable C++ C Python Java TypeScript Rust

  • Easy: Simple to understand, includes convenient macro PW_TRY

  • Efficient: No memory allocation, no exceptions

  • Established: Just like absl::Status, deployed extensively at Google

  • Customizable: Create custom status types for domain-specific error codes with pw_enum

pw::Status is Pigweed’s error propagation primitive, enabling exception-free error handling. The primary feature of pw_status is the pw::Status class, a simple, zero-overhead status object that wraps a status code, and the PW_TRY macro. For example:

#include "pw_status/status.h"

pw::Status ImuEnable() {
  if (!device_has_imu) {
    return Status::FailedPrecondition();
  }
  PW_TRY(ImuSpiSendEnable());  // Propagates failure on non-OK status.
  return pw::OkStatus();
}

void Initialize() {
  if (auto status = ImuEnable(); status.ok()) {
    PW_LOG_INFO("Imu initialized successfully")
  } else {
    if (status.IsFailedPrecondition()) {
      PW_LOG_WARNING("No IMU present");
    } else {
      PW_LOG_ERROR("Unknown error initializing IMU: %d", status.code());
    }
  }
}

pw_status provides an implementation of status in every supported Pigweed language, including C, Rust, TypeScript, Java, and Python.

Pigweed’s status matches Google’s standard status codes (see the Google APIs repository). These codes are used extensively in Google projects including Abseil (status/status.h) and gRPC (doc/statuscodes.md).

Get Started & Guides

Integrate pw_status into your project, see common uses

Get started
API Reference

Detailed description of pw_status’s methods.

Reference

Quick reference#

See Status codes for the precise semantics of each error, as well as how to spell the status in each of our supported languages. Click on the status names below to jump directly to that error’s reference.

Status

Code

Description

OK

0

Operation succeeded

CANCELLED

1

Operation was cancelled, typically by the caller

UNKNOWN

2

Unknown error occurred. Avoid this code when possible.

INVALID_ARGUMENT

3

Argument was malformed; e.g. invalid characters when parsing integer

DEADLINE_EXCEEDED

4

Deadline passed before operation completed

NOT_FOUND

5

The entity that the caller requested (e.g. file or directory) is not found

ALREADY_EXISTS

6

The entity that the caller requested to create is already present

PERMISSION_DENIED

7

Caller lacks permission to execute action

RESOURCE_EXHAUSTED

8

Insufficient resources to complete operation; e.g. supplied buffer is too small

FAILED_PRECONDITION

9

System isn’t in the required state; e.g. deleting a non-empty directory

ABORTED

10

Operation aborted due to e.g. concurrency issue or failed transaction

OUT_OF_RANGE

11

Operation attempted out of range; e.g. seeking past end of file

UNIMPLEMENTED

12

Operation isn’t implemented or supported

INTERNAL

13

Internal error occurred; e.g. system invariants were violated

UNAVAILABLE

14

Requested operation can’t finish now, but may at a later time

DATA_LOSS

15

Unrecoverable data loss occurred while completing the requested operation

UNAUTHENTICATED

16

Caller does not have valid authentication credentials for the operation

Custom status codes with StatusBase#

pw::StatusBase is a base class template that provides Pigweed status semantics for custom code types, such as domain-specific error enums or integer codes. It provides ok() checks, [[nodiscard]] enforcement, and error propagation with PW_TRY and pw::Result. Pigweed’s standard pw::Status is itself implemented as a concrete class inheriting from StatusBase<Status, PW_STATUS_OK>.

Each StatusBase specialization designates one code value as success (ok() == true). Custom status types are constructed from a status code (e.g. MyStatus(MyStatus::Code::kFailed)). They default construct to the OK code. The underlying code type is aliased as MyStatus::Code.

Enum status codes#

Enums are recommended for custom status codes because they prevent arbitrary or unknown values and allow the compiler to warn about unhandled cases in switch statements.

The recommended way to define an enum-based status type is with pw_enum’s PW_ENUM_STATUS macro (see Defining custom status types). PW_ENUM_STATUS generates a StatusBase-derived class from an enum, complete with a factory function for each error code, Is*() query functions, stringification, and tokenized logging support.

If not using pw_enum, pw::StatusOf (guide) is a final, [[nodiscard]] status class template that wraps an existing enum:

enum class DriverError : uint8_t {
  kOk,
  kBusFault,
  kTimeout,
};

using DriverStatus = pw::StatusOf<DriverError::kOk>;

constexpr DriverStatus WriteByte(uint8_t byte) {
  if (byte == 0xFF) {
    return DriverStatus(DriverError::kBusFault);
  }
  return DriverStatus();
}

// Propagate custom errors using PW_TRY.
constexpr DriverStatus SendPacket(uint8_t header, uint8_t payload) {
  PW_TRY(WriteByte(header));
  PW_TRY(WriteByte(payload));
  return DriverStatus();
}

Callers inspect the status or compare it directly against the underlying enum:

DriverStatus status = SendPacket(0x01, 0x02);
if (status.ok()) {
  // Packet sent successfully.
} else if (status == DriverError::kBusFault) {
  // Handle bus fault.
}

To fully customize the status class, subclass StatusBase directly.

Non-enum status codes#

Enums are recommended for status codes because they prevent arbitrary or unknown values and allow the compiler to warn about unhandled cases in switch statements. However, StatusBase also supports non-enum types such as integers when interfacing with existing protocols or numeric error codes. For example, HTTP status codes can be represented with 200 designating success:

// StatusBase can also be used with non-enum types such as integers.
class [[nodiscard]] HttpStatus final : public pw::StatusBase<HttpStatus, 200> {
 public:
  constexpr HttpStatus() = default;
  explicit constexpr HttpStatus(int code) : StatusBase(code) {}
};

constexpr HttpStatus FetchResource(bool resource_found) {
  if (!resource_found) {
    return HttpStatus(404);  // Explicit construction for non-OK codes
  }
  return HttpStatus();  // Defaults to 200 OK
}

Callers handle non-enum statuses in the same way:

HttpStatus status = FetchResource(true);
if (status.ok()) {
  // Request succeeded (200).
} else {
  // Handle HTTP error code (e.g. status.code() == 404).
}

Should I use a custom status?#

Custom status types offer significant advantages, but are not a fit for every scenario or project.

Advantages

  • Resolve ambiguity: Each distinct error can map to its own code. No more wondering which resource was exhausted for RESOURCE_EXHAUSTED.

  • Better API contracts for callers: APIs only produce codes that make sense. Callers do not have consider codes that will never be returned.

  • Better API contracts for implementers: API implementers are constrained to defined codes and cannot return codes that do not apply. No need to check that an implementation returned a supported code.

  • Thoughtful layering of status domains: Prevent users from mindlessly forwarding errors between API layers with PW_TRY, which loses meaning and specificity.

Disadvantages

  • Cognitive load: Users have to learn different status codes for different APIs.

  • Enum proliferation: Custom statuses could lead to a proliferation of enums, even when fewer status types would be better.

Whether and how to use custom status types is ultimately a project decision.