How absl::StatusOr<T> Handles Error Propagation in Abseil C++

Error propagation in absl::StatusOr<T> works by storing a non-OK absl::Status inside a discriminated union, forwarding that status during copy/move operations, and enforcing explicit handling through runtime checks that crash or throw BadStatusOrAccess if the value is accessed while in an error state.

The absl::StatusOr<T> class in the Abseil C++ library provides a monadic container for error propagation, acting as a discriminated union that holds either a value of type T or an absl::Status indicating failure. This pattern eliminates the need for output parameters or exceptions while ensuring that error states flow explicitly through your codebase. Understanding how absl::StatusOr propagates errors requires examining its internal storage mechanics, construction constraints, and strict value-access semantics.

Construction from Error Status

absl::StatusOr<T> propagates errors starting at construction. The class provides a template constructor that accepts any type convertible to absl::Status, enabling direct initialization from error values.

The Status-Accepting Constructor

In absl/status/statusor.h (lines 63–71), the constructor uses SFINAE traits to validate status conversions:

template <typename U = absl::Status,
          std::enable_if_t<internal_statusor::IsConstructionFromStatusValid<
                                false, T, U>::value, int> = 0>
StatusOr(U&& v) : Base(std::forward<U>(v)) {}

This constructor delegates to internal machinery that calls EnsureNotOk(), rejecting accidental construction with an OK status. The implementation guarantees that a StatusOr object can only hold a status if that status represents an actual error condition, preventing the ambiguous state of "success without a value."

Querying the Object State

Once constructed, absl::StatusOr provides explicit methods to inspect whether it holds a value or an error, enabling disciplined error propagation throughout the call stack.

Checking Success with ok() and status()

The ok() method in statusor.h (lines 57–73) provides a fast boolean check:

ABSL_MUST_USE_RESULT bool ok() const { return this->status_.ok(); }

The status() method returns the stored absl::Status object, yielding OkStatus() when a value is present or the error code when in a failed state. These accessors allow callers to branch on error conditions before attempting value extraction:

absl::StatusOr<int> result = ComputeValue();
if (!result.ok()) {
  return result.status();  // Propagate error upward
}

Value Access and Safety Guarantees

absl::StatusOr enforces strict semantics that prevent silent error dropping. Any attempt to access the stored value while in an error state triggers immediate program termination or an exception.

Runtime Enforcement with EnsureOk()

The value accessors—including value(), operator*(), and operator->()—internally call self().EnsureOk(), implemented in the OperatorBase class within absl/status/internal/statusor_internal.h. If the object is not OK, this mechanism throws absl::BadStatusOrAccess or crashes the process, depending on configuration.

This design ensures that error propagation cannot be accidentally ignored. You must explicitly check ok() before dereferencing, or the program will halt:

absl::StatusOr<std::string> data = LoadFile("config.txt");
// Dangerous: Will crash if LoadFile failed
std::string contents = *data;  // Calls EnsureOk() internally

Error Propagation Through Assignment

When copying or moving StatusOr objects, the implementation propagates errors by inspecting the source object's state and forwarding the appropriate payload.

Copy Assignment Semantics

In absl/status/statusor.cc (lines 35–41), the copy assignment logic explicitly branches on the source state:

if (other.ok()) {
  this->Assign(*other);          // Copy the value
} else {
  this->AssignStatus(other.status()); // Propagate the error
}

This ensures that if the source contains an error, the destination receives that same error status rather than a default-constructed value.

Move Assignment Semantics

The move assignment path (lines 44–50) follows identical logic but transfers ownership:

if (other.ok()) {
  this->Assign(*std::move(other));
} else {
  this->AssignStatus(std::move(other).status());
}

Move semantics preserve error propagation while avoiding unnecessary copies of the absl::Status object, maintaining efficiency when returning StatusOr from functions.

Safe Access Patterns

Beyond strict enforcement, absl::StatusOr provides utilities for handling errors gracefully without throwing exceptions.

Providing Fallbacks with value_or()

The value_or() method, defined in statusor.h (lines 73–80), returns the held value if OK, otherwise constructs the supplied default:

absl::StatusOr<int> maybe = ParseInt(user_input);
int value = maybe.value_or(0);  // Returns 0 if parsing failed

This pattern allows local error recovery without polluting the calling function with explicit error checking.

Suppressing Unused Error Warnings

The IgnoreError() method acts as a no-op to silence static analysis tools that warn about ignored return values:

GetOptionalConfig().IgnoreError();  // Explicitly acknowledge we don't care about errors here

Internal Storage Architecture

The error propagation mechanism relies on StatusOrData in absl/status/internal/statusor_internal.h, which manages the discriminated union storage. This internal class handles the complex lifecycle of holding either a T or an absl::Status, ensuring proper destruction and move semantics while preventing the simultaneous existence of both states.

Summary

  • Explicit error storage: absl::StatusOr<T> holds either a value of type T or a non-OK absl::Status, rejecting construction with OK status via EnsureNotOk().
  • Strict access semantics: Value accessors invoke EnsureOk(), crashing or throwing BadStatusOrAccess if accessed in an error state.
  • Automatic propagation: Copy and move operations in statusor.cc check ok() and forward errors via AssignStatus(), ensuring errors flow through assignment chains.
  • Safe utilities: value_or() provides fallback values without exceptions, while IgnoreError() satisfies static analysis requirements.

Frequently Asked Questions

What happens if I try to access the value of a StatusOr containing an error?

Accessing the value through value(), operator*(), or operator->() calls EnsureOk() internally. If the object is not OK, the program will either crash or throw absl::BadStatusOrAccess, depending on your build configuration. This guarantees that error states cannot be silently ignored.

Can I construct a StatusOr with an OK status and no value?

No. The constructor that accepts absl::Status uses EnsureNotOk() to reject OK statuses at runtime. A StatusOr must contain either a valid value of type T or a non-OK status indicating failure. This invariant prevents the ambiguous state of "success with no data."

How does StatusOr handle errors when copying between different types?

When copying or assigning a StatusOr<U> to a StatusOr<T>, the implementation checks the source's ok() state. If the source contains an error, it propagates that error status regardless of the type difference. If the source is OK, it attempts to convert the value from U to T, which may fail if the types are incompatible.

Is there a performance cost to using StatusOr for error propagation?

The overhead is minimal. absl::StatusOr uses a discriminated union with move semantics to avoid heap allocation. The ok() check compiles to a simple integer comparison, and the ABSL_MUST_USE_RESULT attribute ensures errors are handled at compile time where possible. The primary cost is the branch prediction when checking status, which is comparable to traditional error code checking.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →