grpc/grpc-java · error · VerifyException
Status code is not valid
Error message
Status code ${status} is not valid What it means
ServiceConfigUtil.getStatusCodesFromList parses the retryable status codes list from a gRPC service config. Each entry must be either an integer equal to a valid Status code value or a string naming a Status.Code enum constant. When an integer entry does not match any real status code value, a VerifyException is thrown during config parsing.
Solutions
- Check each numeric entry in retryableStatusCodes against the gRPC Status code value table (e.g. 0=OK, 3=INVALID_ARGUMENT, 8=RESOURCE_EXHAUSTED, 14=UNAVAILABLE, 16=UNAUTHENTICATED)
- Prefer string names like "UNAVAILABLE" over numeric values to eliminate value-table mistakes
- Remove or correct the offending entry and redeploy the service config
- Validate the full service config with the grpc service config schema/validator before pushing it
Example fix
// before "retryableStatusCodes": [14, 99] // after "retryableStatusCodes": ["UNAVAILABLE", "RESOURCE_EXHAUSTED"]
Defensive patterns
Strategy: validation
Validate before calling
// Validate before trusting config
for (Object status : statusCodes) {
if (status instanceof Integer) {
int v = (Integer) status;
if (io.grpc.Status.fromCodeValue(v) == io.grpc.Status.UNKNOWN && v != io.grpc.Status.UNKNOWN.getCode().value()) {
throw new IllegalArgumentException("Not a gRPC status code value: " + v);
}
} else if (status instanceof String) {
io.grpc.Status.Code.valueOf((String) status); // throws if invalid
} else {
throw new IllegalArgumentException("Bad type: " + status.getClass());
}
} Try / catch
try {
codes = ServiceConfigUtil.getListOfStatusCodesAsSet(rawList);
} catch (VerifyException e) {
log.error("Bad retryableStatusCodes in service config: " + e.getMessage());
throw new ConfigInvalidException(e);
} Prevention
- Use string status names instead of numeric values in service configs
- Lint service configs against the gRPC service config schema in CI
- Keep a lookup table of gRPC code values when generating configs
When it happens
Trigger: A service config's retryPolicy.retryableStatusCodes (or similar) list contains a numeric entry whose value does not correspond to a gRPC status code value (e.g. 7 corresponds to PERMISSION_DENIED, but 99 or 1234 does not). Reached via getListOfStatusCodesAsSet during service config resolution.
Common situations: Hand-written or tool-generated service config JSON with a typo in a numeric status code value; a config generated against a newer/different status code table; copy-pasted values from HTTP status codes instead of gRPC status codes.
Understand the failure class
Background: Invalid enum value errors: "Unknown type", "Invalid scope", "must be one of" — when a string is not on the library's allowed list — this error's family across 23 libraries.
Related errors
- Can not convert status code
- key ' ' missing in
- There are fields in a LoadBalancingConfig object. Exactly…
- wrong type
- A key manager is required
AI-assisted analysis of grpc/grpc-java@64daddc1f3 (2026-09-08).
Data as JSON: /api/errors/8795230282abcee0.
Report an issue: GitHub.
Appendix: source
Thrown at core/src/main/java/io/grpc/internal/ServiceConfigUtil.java:170
}
return getStatusCodesFromList(statuses);
}
private static Set<Status.Code> getStatusCodesFromList(List<?> statuses) {
EnumSet<Status.Code> codes = EnumSet.noneOf(Status.Code.class);
for (Object status : statuses) {
Status.Code code;
if (status instanceof Double) {
Double statusD = (Double) status;
int codeValue = statusD.intValue();
verify((double) codeValue == statusD, "Status code %s is not integral", status);
code = Status.fromCodeValue(codeValue).getCode();
verify(code.value() == statusD.intValue(), "Status code %s is not valid", status);
} else if (status instanceof String) {
try {
code = Status.Code.valueOf((String) status);
} catch (IllegalArgumentException iae) {
throw new VerifyException("Status code " + status + " is not valid", iae);
}
} else {
throw new VerifyException(
"Can not convert status code " + status + " to Status.Code, because its type is "
+ status.getClass());
}
codes.add(code);
}
return Collections.unmodifiableSet(codes);
}
static Set<Status.Code> getRetryableStatusCodesFromRetryPolicy(Map<String, ?> retryPolicy) {
String retryableStatusCodesKey = "retryableStatusCodes";
Set<Status.Code> codes = getListOfStatusCodesAsSet(retryPolicy, retryableStatusCodesKey);
verify(codes != null, "%s is required in retry policy", retryableStatusCodesKey);
verify(!codes.contains(Status.Code.OK), "%s must not contain OK", retryableStatusCodesKey);
return codes;
}View on GitHub (pinned to 64daddc1f3)