orElseThrow()

Unwrap an Optional or throw: a NoSuchElementException by default, or your own not-found exception that becomes a 404.

Signature
public T orElseThrow()
public <X extends Throwable> T orElseThrow(Supplier<? extends X> exceptionSupplier) throws X
  • Added in Java Java 8

Parameters

ParameterTypeDescription
$exceptionSupplieroptionalSupplier<? extends X>Called only when no value is present; returns the exception to throw.

Return value

TThe value, if present.

Errors

orElseThrow() throws NoSuchElementException if no value is present. orElseThrow(exceptionSupplier) throws the supplied exception X if no value is present, and NullPointerException if no value is present and exceptionSupplier is null.

Changelog

  • PHP Java 10No-argument orElseThrow() added. It behaves like get() but its name makes the possible exception obvious, and it is the preferred alternative to get().

orElseThrow returns the value if there is one and throws otherwise. It is the right choice when a missing value is an error the caller cannot paper over with a default: the order id in the URL does not exist, a required setting is not configured.

Two overloads

The no-argument form, added in Java 10, throws NoSuchElementException with the message No value present. It does exactly what get() does, but the name tells the reader it can throw, which is why it is the recommended replacement for get(). Use it after you have already checked, or where an empty value really would be a bug.

The supplier form lets you choose the exception. The supplier runs only when the Optional is empty, so building a message is free on the happy path:

Account account = accounts.findById(id)
        .orElseThrow(() -> new NotFoundException("account " + id + " not found"));

Its signature is <X extends Throwable> T orElseThrow(Supplier<? extends X>) throws X, so it can throw checked exceptions too. If the supplier returns an IOException, the compiler requires the enclosing method to declare or catch IOException.

Back-end angle

This is how most APIs turn "not found" into a 404. The repository returns Optional, the service calls orElseThrow with a domain exception such as NotFoundException, and one exception handler maps that exception to a 404 response (or a Problem Details body). Handlers stay short because none of them builds error responses by hand. Spring's ResponseStatusException and @ExceptionHandler follow the same shape.

Mistakes people make

  • Using bare orElseThrow() on user input. The client gets a 500 and the log says only No value present, instead of a 404 that names the missing resource.
  • Writing orElseThrow(new NotFoundException(...)). That passes an exception, not a supplier, and does not compile. Wrap it in a lambda or a constructor reference such as orElseThrow(IllegalStateException::new).
  • Putting sensitive details such as raw tokens in the exception message, which then ends up in logs and error bodies.

Example

Try it yourself
Loading editor…
Press Run to execute the code.

When to use it

  • Turning a missing entity in GET /orders/{id} into a NotFoundException that a global handler maps to 404.
  • Stopping service startup with a clear message when a required environment variable is missing.
  • Rejecting a request with 401 when the Authorization header is absent or uses the wrong scheme.

Frequently asked questions

What is the difference between Optional.get() and orElseThrow()?

None in behaviour: both return the value or throw NoSuchElementException with 'No value present'. orElseThrow() was added in Java 10 because its name makes the exception obvious, and it is the preferred one.

How do I throw a custom exception if an Optional is empty?

Pass a supplier: optional.orElseThrow(() -> new NotFoundException("user " + id)). The supplier is only called when the Optional is empty, so building the message costs nothing on the happy path.

Can orElseThrow throw a checked exception?

Yes. Its signature is <X extends Throwable> T orElseThrow(Supplier<? extends X>) throws X, so a supplier that creates an IOException makes the call throw IOException, which the method must declare or catch.

More examples

Failing fast on required configuration

A service should refuse to start without its required settings rather than fail on the first request. filter turns a blank value into empty, so it is reported the same way as a missing one.

Example · java
String required(Map<String, String> env, String name) {
    return Optional.ofNullable(env.get(name))
            .filter(v -> !v.isBlank())
            .orElseThrow(() -> new IllegalStateException(name + " is not set"));
}

void main() {
    Map<String, String> env = Map.of("DB_URL", "jdbc:postgresql://db:5432/shop", "DB_USER", "");
    IO.println(required(env, "DB_URL"));
    try {
        required(env, "DB_USER");
    } catch (IllegalStateException e) {
        IO.println("startup failed: " + e.getMessage());
    }
}
// Output:
// jdbc:postgresql://db:5432/shop
// startup failed: DB_USER is not set

Throwing a checked exception

orElseThrow is declared as throws X, so the compiler knows it can throw IOException and requires loadTemplate to declare it. The first call succeeds before the second throws.

Example · java
final Map<String, String> templates = Map.of("welcome", "Hi {name}");

String loadTemplate(String name) throws IOException {
    return Optional.ofNullable(templates.get(name))
            .orElseThrow(() -> new IOException("template " + name + " missing"));
}

void main() {
    try {
        IO.println(loadTemplate("welcome"));
        IO.println(loadTemplate("reset"));
    } catch (IOException e) {
        IO.println("IOException: " + e.getMessage());
    }
}
// Output:
// Hi {name}
// IOException: template reset missing

Extracting a bearer token or rejecting the request

ofNullable wraps the possibly missing header, filter and map shape it, and orElseThrow turns both a wrong scheme and a missing header into the same 401 error. The message deliberately does not echo the header value.

Example · java
String bearerToken(String authorization) {
    return Optional.ofNullable(authorization)
            .filter(h -> h.startsWith("Bearer "))
            .map(h -> h.substring("Bearer ".length()))
            .orElseThrow(() -> new IllegalArgumentException("401 missing bearer token"));
}

void main() {
    IO.println(bearerToken("Bearer abc.def.ghi"));
    for (String header : Arrays.asList("Basic dXNlcjpwYXNz", null)) {
        try {
            bearerToken(header);
        } catch (IllegalArgumentException e) {
            IO.println(e.getMessage());
        }
    }
}
// Output:
// abc.def.ghi
// 401 missing bearer token
// 401 missing bearer token

Discussion

  • Be the first to comment on this lesson.