# How to Migrate from Raw Status Pointers to StatusOr<T> in Abseil C++

> Migrate from raw status pointers to StatusOr<T> in Abseil C++ to enforce error checking, resolve pointer lifetime issues, and bundle nullable values with status codes.

- Repository: [Abseil/abseil-cpp](https://github.com/abseil/abseil-cpp)
- Tags: migration-guide
- Published: 2026-07-14

---

**Replace `const absl::Status*` return types with `absl::StatusOr<T>` to enforce mandatory error checking, eliminate pointer lifetime ambiguity, and bundle nullable values with their corresponding status codes.**

The Abseil C++ library provides modern error-handling primitives that have superseded legacy raw pointer patterns. If your codebase currently relies on `const absl::Status*` to indicate success (`nullptr`) or failure (non-null pointer), you should migrate to `absl::StatusOr<T>` to align with current Abseil best practices and leverage compile-time safety guarantees.

## Why Raw Status Pointers Are Problematic

Historically, some Abseil-based codebases used a convention where functions returned a **raw pointer to a constant `absl::Status`** (`const absl::Status*`). This pattern interpreted return values as follows:

- **`nullptr`**: Success—callers should proceed with the operation.
- **Non-null pointer**: Failure—the pointed-to `absl::Status` contains error details.

This approach introduces significant risks:

- **Lifetime uncertainty**: Callers cannot determine whether the returned pointer refers to a static object, stack temporary, or heap allocation without inspecting the implementation.
- **Uncheckable results**: The compiler cannot enforce that callers examine the pointer, leading to ignored error conditions.
- **Awkward value returns**: Functions needing to return both a value and an error status require additional out-parameters or wrapper structs.

`absl::StatusOr<T>` solves these issues by unifying the value and status into a single type annotated with `[[nodiscard]]` (via `ABSL_MUST_USE_RESULT`), ensuring callers explicitly handle errors while eliminating manual memory management concerns.

## Step-by-Step Migration Guide

### Identify Legacy APIs

Search your codebase for function signatures containing `const absl::Status*` or `absl::Status*`. Common examples appear in internal logging utilities such as [`absl/log/internal/check_op.h`](https://github.com/abseil/abseil-cpp/blob/main/absl/log/internal/check_op.h), which still exposes legacy status pointer interfaces. Mark these functions as migration targets.

### Select the Appropriate Value Type

Determine what `T` should represent in your new `absl::StatusOr<T>` signature:

- **Out-parameter replacement**: If the original function wrote results into an out-parameter (e.g., `std::string* out`), `T` becomes that parameter's type (`std::string`).
- **Error-only returns**: If the function only indicated success or failure without returning data, use `absl::Status` as `T` (though in most cases, simply returning `absl::Status` directly is preferable).

### Update Function Signatures

Replace the raw pointer return type with `absl::StatusOr<T>`. For example:

```cpp
// Old pattern
const absl::Status* ReadFile(absl::string_view path,
                            std::string* out);

// Modern Abseil pattern
absl::StatusOr<std::string> ReadFile(absl::string_view path);

```

### Rewrite Implementation Logic

Eliminate raw status allocation and instead construct `StatusOr<T>` objects directly:

**Success path**—return the value directly (implicitly constructs an OK status):

```cpp
return std::string(contents);
// Or explicitly:
return absl::StatusOr<std::string>(contents);

```

**Error path**—return a status object:

```cpp
return absl::InvalidArgumentError("cannot open file");

```

Do not allocate `absl::Status` objects on the heap or return raw pointers. The `StatusOr` implementation in [`absl/status/internal/statusor_internal.h`](https://github.com/abseil/abseil-cpp/blob/main/absl/status/internal/statusor_internal.h) manages status storage inline without heap allocation for the common case.

### Modernize Call Sites

Transform caller code from pointer checks to status checks:

**Old usage:**

```cpp
const absl::Status* err = ReadFile(path, &out);
if (err) {
    LOG(ERROR) << err->ToString();
    return;
}

```

**New usage:**

```cpp
absl::StatusOr<std::string> result = ReadFile(path);
if (!result.ok()) {
    LOG(ERROR) << result.status().ToString();
    return;
}
std::string out = *result;        // or result.value()
std::string alt = result.value(); // explicit accessor

```

Key accessors include:
- `result.ok()`—returns `true` if the status is OK.
- `result.value()` or `*result`—dereferences the contained value (throws `BadStatusOrAccess` if not OK).
- `result->member`—allows member access on the contained value.

## Simplifying Error Propagation

When functions chain `StatusOr` operations, use the `ASSIGN_OR_RETURN` macro defined in [`absl/status/statusor.h`](https://github.com/abseil/abseil-cpp/blob/main/absl/status/statusor.h) to reduce boilerplate:

```cpp
absl::StatusOr<int> ComputeValue() {
    ASSIGN_OR_RETURN(int x, ParseInt());  // Returns early on error
    ASSIGN_OR_RETURN(int y, ParseFloat());
    return x + y;
}

```

This macro checks the `StatusOr` result, assigns the contained value to the specified variable if OK, or returns the error status immediately otherwise.

## Performance and Safety Guarantees

`absl::StatusOr<T>` provides several mechanical advantages enforced by the Abseil implementation:

- **Mandatory result checking**: The `[[nodiscard]]` attribute (exposed as `ABSL_MUST_USE_RESULT` in [`absl/status/statusor.h`](https://github.com/abseil/abseil-cpp/blob/main/absl/status/statusor.h)) triggers compiler warnings if return values are discarded.
- **Zero-overhead abstraction**: As implemented in [`absl/status/internal/statusor_internal.h`](https://github.com/abseil/abseil-cpp/blob/main/absl/status/internal/statusor_internal.h), the type uses a union layout that stores the status and value inline without additional heap allocation for most types.
- **Exception safety**: The implementation provides strong exception guarantees for value types with non-throwing move constructors.

## Summary

- **Locate** functions returning `const absl::Status*` using static analysis or grep, particularly in legacy files like [`absl/log/internal/check_op.h`](https://github.com/abseil/abseil-cpp/blob/main/absl/log/internal/check_op.h).
- **Replace** signatures with `absl::StatusOr<T>` where `T` represents the value previously returned via out-parameter.
- **Return** values directly on success and status objects on error, avoiding raw pointer allocation.
- **Access** results using `ok()`, `value()`, and dereference operators, ensuring explicit error handling.
- **Propagate** errors concisely using `ASSIGN_OR_RETURN` from [`absl/status/statusor.h`](https://github.com/abseil/abseil-cpp/blob/main/absl/status/statusor.h).
- **Verify** the migration by running the full test suite to confirm all error paths remain functional.

## Frequently Asked Questions

### What is the difference between StatusOr<T> and returning a Status*?

`absl::StatusOr<T>` bundles a status code and an optional value into a single value-type object with mandatory result checking. Raw `absl::Status*` pointers require manual lifetime management and provide no compile-time enforcement that callers check for errors, allowing null pointer dereferences or silent failures.

### How do I handle functions that only return errors without values?

If a function only signals success or failure without returning data, prefer returning `absl::Status` directly rather than `absl::StatusOr<absl::Status>`. The `absl::Status` type in [`absl/status/status.h`](https://github.com/abseil/abseil-cpp/blob/main/absl/status/status.h) represents these semantics more directly and avoids unnecessary template instantiation.

### Is StatusOr<T> efficient for large objects?

Yes. The implementation in [`absl/status/internal/statusor_internal.h`](https://github.com/abseil/abseil-cpp/blob/main/absl/status/internal/statusor_internal.h) stores the value inline within the object footprint. For large or expensive-to-copy types, `absl::StatusOr<std::unique_ptr<T>>` or `absl::StatusOr<std::string>` (which uses move semantics) provides efficient transfer without heap allocation overhead.

### Where can I find examples of legacy Status* usage in Abseil?

The file [`absl/log/internal/check_op.h`](https://github.com/abseil/abseil-cpp/blob/main/absl/log/internal/check_op.h) contains examples of legacy APIs that utilize raw `absl::Status*` returns. Studying these implementations provides concrete context for how the old pattern functioned and why the Abseil team recommends migrating to `absl::StatusOr<T>` for new code.