grpc/grpc-java · error · IllegalStateException
No value present.
Error message
No value present.
What it means
StatusOr.getValue() returns the contained value only when no error Status is set. If the StatusOr holds a status instead of a value (hasValue() == false), calling getValue() throws this IllegalStateException. The javadoc explicitly requires checking hasValue() first.
Solutions
- Always check hasValue() before calling getValue()
- Use getStatus() to inspect and log the error status when hasValue() is false
- Propagate the status (e.g. to the stream/stream-trailers) instead of unwrapping
- In tests, assert hasValue() before asserting on the value
Example fix
// before
Address addr = statusOr.getValue(); // throws when resolution failed
// after
if (statusOr.hasValue()) {
Address addr = statusOr.getValue();
} else {
Status s = statusOr.getStatus(); // handle/log UNAVAILABLE etc.
} Defensive patterns
Strategy: type-guard
Validate before calling
boolean usable = statusOr != null && statusOr.hasValue();
Type guard
static <T> T getOrThrow(StatusOr<T> or, Supplier<? extends RuntimeException> onAbsent) {
return or.hasValue() ? or.getValue() : onAbsent.get();
} Try / catch
try {
value = statusOr.getValue();
} catch (IllegalStateException e) {
Status s = statusOr.getStatus(); // inspect/handle error status
} Prevention
- Always gate getValue() behind hasValue()
- On the false branch read getStatus() and propagate/log it
- Prefer a helper like getOrDefault() to avoid raw unwrap at call sites
When it happens
Trigger: Calling getValue() on a StatusOr produced from a failed operation — e.g. a failed name resolution (onResult), a lookup that returned an error status — without first checking hasValue(). Callers listed include resolvedAddresses, servers, clusterConfig on error paths.
Common situations: NameResolver results that failed with UNAVAILABLE or similar; xDS cluster config lookup failures; treating a StatusOr as always-successful after refactoring code that previously used exceptions or nullable returns.
Understand the failure class
Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.
Related errors
- A key manager is required
- A terminal HttpFilter must be the last filter
- Address is not an IP
- Address types of NameResolver
- All xds transports for authority are in backoff
AI-assisted analysis of grpc/grpc-java@64daddc1f3 (2026-09-08).
Data as JSON: /api/errors/70bf6ba72c220949.
Report an issue: GitHub.
Appendix: source
Thrown at api/src/main/java/io/grpc/StatusOr.java:59
/** Construct from a non-Ok status. */
public static <T> StatusOr<T> fromStatus(Status status) {
StatusOr<T> result = new StatusOr<T>(checkNotNull(status, "status"), null);
checkArgument(!status.isOk(), "cannot use OK status: %s", status);
return result;
}
/** Returns whether there is a value. */
public boolean hasValue() {
return status == null;
}
/**
* Returns the value if set or throws exception if there is no value set. This method is meant
* to be called after checking the return value of hasValue() first.
*/
public T getValue() {
if (status != null) {
throw new IllegalStateException("No value present.");
}
return value;
}
/** Returns the status. If there is a value (which can be null), returns OK. */
public Status getStatus() {
return status == null ? Status.OK : status;
}
/**
* Note that StatusOr containing statuses, the equality comparision is delegated to
* {@link Status#equals} which just does a reference equality check because equality on
* Statuses is not well defined.
* Instead, do comparison based on their Code with {@link Status#getCode}. The description and
* cause of the Status are unlikely to be stable, and additional fields may be added to Status
* in the future.
*/
@OverrideView on GitHub (pinned to 64daddc1f3)