grpc/grpc-java · error · IllegalArgumentException
Multiple URI SAN values found in the leaf cert.
Error message
Multiple URI SAN values found in the leaf cert.
What it means
SpiffeUtil.extractSpiffeId derives a SPIFFE ID from the leaf certificate's Subject Alternative Names. The SPIFFE spec requires exactly one URI SAN, so if the leaf certificate contains more than one URI SAN entry this IllegalArgumentException is thrown.
Source
Thrown at core/src/main/java/io/grpc/internal/SpiffeUtil.java:146
*
* @param certChain certificate chain to extract SPIFFE ID from
*/
public static Optional<SpiffeId> extractSpiffeId(X509Certificate[] certChain)
throws CertificateParsingException {
checkArgument(checkNotNull(certChain, "certChain").length > 0, "certChain can't be empty");
Collection<List<?>> subjectAltNames = certChain[0].getSubjectAlternativeNames();
if (subjectAltNames == null) {
return Optional.absent();
}
String uri = null;
// Search for the unique URI SAN.
for (List<?> altName : subjectAltNames) {
if (altName.size() < 2 ) {
continue;
}
if (URI_SAN_TYPE.equals(altName.get(0))) {
if (uri != null) {
throw new IllegalArgumentException("Multiple URI SAN values found in the leaf cert.");
}
uri = (String) altName.get(1);
}
}
if (uri == null) {
return Optional.absent();
}
return Optional.of(parse(uri));
}
/**
* Loads a SPIFFE trust bundle from a file, parsing it from the JSON format.
* In case of success, returns {@link SpiffeBundle}.
* If any element of the JSON content is invalid or unsupported, an
* {@link IllegalArgumentException} is thrown and the entire Bundle is considered invalid.
*
* @param trustBundleFile the file path to the JSON file containing the trust bundle
* @see <a href="https://github.com/spiffe/spiffe/blob/main/standards/SPIFFE_Trust_Domain_and_Bundle.md">JSON format</a>View on GitHub (pinned to 64daddc1f3)
Solutions
- Reissue the leaf certificate with exactly one URI SAN containing the SPIFFE ID (spiffe://trust-domain/workload)
- Remove extra URI SAN entries from the certificate template at your CA
- Verify with: openssl x509 -in cert.pem -text | grep -A2 'URI' — expect exactly one URI entry
Example fix
// before (openssl req san config) subjectAltName = URI:spiffe://ns/default/sa/a, URI:https://example.com // after subjectAltName = URI:spiffe://ns/default/sa/a
Defensive patterns
Strategy: try-catch
Validate before calling
// Inspect leaf cert SANs before use
Collection<List<?>> sans = cert.getSubjectAlternativeNames();
long uriCount = sans == null ? 0 : sans.stream()
.filter(s -> s.size() >= 2 && s.get(0) instanceof Integer && (Integer) s.get(0) == 6)
.count();
if (uriCount > 1) throw new IllegalStateException("Leaf cert has " + uriCount + " URI SANs"); Try / catch
try {
Optional<String> spiffeId = SpiffeUtil.extractSpiffeId(certs);
} catch (IllegalArgumentException e) {
log.error("Leaf cert has multiple URI SANs — reissue with exactly one", e);
failClosed();
} Prevention
- Issue leaf certs with exactly one URI SAN holding the SPIFFE ID
- Audit CA templates for extra URI SAN entries
- Check certs with openssl before deploying: openssl x509 -text | grep 'URI:'
When it happens
Trigger: Presenting an mTLS leaf certificate whose SAN extension contains two or more URI-type entries (URI_SAN_TYPE) to extractSpiffeId during SPIFFE-based workload identity extraction.
Common situations: Certificates issued by CAs that add both a SPIFFE URI and another URI SAN; certificate templates with multiple DNS/URI entries; migrating certificates that retained legacy URIs.
Related errors
- Certificate can't be parsed. Certificate loading for trust d
- Peer certificate SAN check failed
- SPIFFE Trust Bundle should be a JSON object. Found: ${type}
- 'kty' parameter must be one of %s but '%s' found. Certificat
- 'kid' parameter must not be set. Certificate loading for tru
AI-assisted analysis of grpc/grpc-java@64daddc1f3 (2026-09-08).
Data as JSON: /api/errors/799f62fd4960f60a.
Report an issue: GitHub.