Best Practices for Using `absl::StatusOr<T>` in Abseil C++

Always check ok() before accessing the value, prefer operator* over value() after validation, and use value_or() for safe defaults.

absl::StatusOr<T> is the core error-handling primitive in the Abseil C++ library. It couples an absl::Status with a value of type T, guaranteeing that either a successful value is present or an error status explains the failure. This article covers essential patterns from the Abseil source code to help you write safe, efficient, and maintainable error-handling code.

Check Success Before Accessing the Value

The absl::StatusOr<T> class provides an ok() accessor at line 71 in absl/status/statusor.h that mirrors absl::Status::ok(). All client code should verify success before dereferencing.

absl::StatusOr<int> GetCount();
absl::StatusOr<int> count = GetCount();

if (count.ok()) {
  // Safe to access value
  int total = *count;
  LOG(INFO) << "Total = " << total;
} else {
  LOG(ERROR) << "Failed: " << count.status();
}

The ABSL_MUST_USE_RESULT attribute ensures callers cannot silently ignore a StatusOr return value.

Prefer operator* and operator-> Over value()

After confirming ok(), use the lightweight dereference operators instead of value(). As noted in the source comments at line 39 of absl/status/statusor.h, the operators provide direct access without the extra exception or termination risk that value() incurs when called on an error state.

// Good: Direct access after checking ok()
if (result.ok()) {
  Process(*result);           // operator*
  result->MemberFunction();    // operator->
}

// Avoid: value() throws or terminates on error
int x = result.value();      // Risky without prior ok() check

Use value_or() for Safe Defaults

When a default value is acceptable on error, use value_or() defined at line 74 in absl/status/statusor.h. This method constructs the default only when necessary, avoiding unnecessary work on the success path.

absl::StatusOr<std::string> ReadConfig();
std::string cfg = ReadConfig().value_or("default.cfg");

This pattern is cleaner than explicit branching when you have a sensible fallback.

Propagate Errors Efficiently

Construct a StatusOr<T> directly from a non-OK absl::Status using the converting constructor at line 63 in absl/status/statusor.h. This forwards failures without extra boilerplate.

absl::StatusOr<std::unique_ptr<Foo>> CreateFoo(int id) {
  if (id < 0) {
    return absl::Status(absl::StatusCode::kInvalidArgument, "negative id");
  }
  return std::make_unique<Foo>(id);  // Implicit conversion to OK StatusOr
}

The move-aware assignment operators at line 40 enable efficient forwarding of temporary values without copies.

Handle StatusOr<T*> with Extra Care

When T is a pointer type, the pointer may be null even when ok() returns true. Always check both conditions before dereferencing, as documented in the source comments at line 70 of absl/status/statusor.h.

absl::StatusOr<std::unique_ptr<Bar>> result = BarFactory();
if (!result.ok()) {
  LOG(ERROR) << result.status();
} else if (*result == nullptr) {
  LOG(ERROR) << "Unexpected null pointer";
} else {
  (*result)->DoWork();
}

Use emplace() for In-Place Construction

When the value type is expensive to copy, use emplace() to build it directly inside the StatusOr. The implementation at line 111 in absl/status/statusor.h preserves correct status handling even if the constructor throws.

absl::StatusOr<std::vector<int>> vec = absl::StatusOr<std::vector<int>>();
vec.emplace(5, 0);  // Constructs vector of 5 zeros with no extra copies

Never Ignore Errors in Production

The IgnoreError() method exists solely for static-analysis suppression and should not be called in production code, as explicitly warned in the comments at line 103 of absl/status/statusor.h.

// Bad: Silences warnings but hides bugs
result.IgnoreError();

// Good: Explicitly handle or CHECK
if (!result.ok()) { /* handle error */ }

Summary

  • Always check ok() before dereferencing a StatusOr.
  • Use operator* and operator-> after validation for zero-overhead access.
  • Leverage value_or() when you need a default value on error.
  • Propagate errors via the converting constructor from absl::Status.
  • Verify pointer validity in StatusOr<T*> even after ok() succeeds.
  • Construct in-place with emplace() to avoid expensive copies.
  • Never use IgnoreError() in production code.

Frequently Asked Questions

What happens if I call value() on an error StatusOr?

The value() method terminates the program or throws an exception depending on your build configuration. As implemented in absl/status/statusor.h, value() performs a check internally and calls FatalMessage() or throws if the StatusOr does not contain a valid value. Use value() only when you have already verified ok() or when program termination is the desired failure mode.

How do I handle StatusOr in a function that returns void?

Use ABSL_MUST_USE_RESULT annotation on your function to force callers to handle the return value. If you must discard a StatusOr legitimately (rare), explicitly check ok() and document why the error is safe to ignore. Never use IgnoreError() as a shortcut.

Is StatusOr suitable for high-performance code?

Yes. absl::StatusOr<T> is designed with zero-overhead abstraction principles. The operator* and operator-> access the value directly with no runtime checks after you verify ok(). The move constructors and emplace() method ensure efficient handling of large or non-copyable types. The implementation in absl/status/statusor.h uses careful placement and union storage to minimize overhead.

Can I use StatusOr with built-in pointers like StatusOr<int*>?

Yes, but you must check both ok() and the pointer value. A StatusOr<int*> can be ok() yet contain a null pointer, since the pointer itself is the value being carried. Always validate *result != nullptr before dereferencing, as noted in the pointer handling comments at line 70 of absl/status/statusor.h.

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 →