grpc/grpc-java · error · CertificateException

Spiffe Trust Map doesn't contain trust domain '%s' from peer

Error message

Spiffe Trust Map doesn't contain trust domain '%s' from peer leaf certificate

What it means

Thrown by XdsX509TrustManager.chooseDelegate when a SPIFFE trust map is configured, the peer's SPIFFE ID was extracted successfully, but the ID's trust domain is not a key in spiffeTrustMapDelegates. The manager refuses to pick a trust manager for an unknown trust domain, failing the TLS handshake rather than silently falling back.

Source

Thrown at xds/src/main/java/io/grpc/xds/internal/security/trust/XdsX509TrustManager.java:329

            String hostName = sniHostName.getAsciiName();
            sniNamesToMatch.add(StringMatcher.newBuilder().setExact(hostName).build());
          }
        }
      }
    }
    return sniNamesToMatch;
  }

  private X509ExtendedTrustManager chooseDelegate(X509Certificate[] chain)
      throws CertificateException {
    if (spiffeTrustMapDelegates != null) {
      Optional<SpiffeUtil.SpiffeId> spiffeId = SpiffeUtil.extractSpiffeId(chain);
      if (!spiffeId.isPresent()) {
        throw new CertificateException("Failed to extract SPIFFE ID from peer leaf certificate");
      }
      String trustDomain = spiffeId.get().getTrustDomain();
      if (!spiffeTrustMapDelegates.containsKey(trustDomain)) {
        throw new CertificateException(String.format("Spiffe Trust Map doesn't contain trust"
            + " domain '%s' from peer leaf certificate", trustDomain));
      }
      return spiffeTrustMapDelegates.get(trustDomain);
    } else {
      return delegate;
    }
  }

  @Override
  public X509Certificate[] getAcceptedIssuers() {
    if (spiffeTrustMapDelegates != null) {
      Set<X509Certificate> result = new HashSet<>();
      for (X509ExtendedTrustManager tm: spiffeTrustMapDelegates.values()) {
        result.addAll(Arrays.asList(tm.getAcceptedIssuers()));
      }
      return result.toArray(new X509Certificate[0]);
    }
    return delegate.getAcceptedIssuers();

View on GitHub (pinned to 64daddc1f3)

Solutions

  1. Add an entry for the peer's trust domain (exactly as it appears in the SPIFFE URI) to the SPIFFE trust map configuration, backed by a trust manager with that domain's root CA
  2. Fix key typos: compare the map keys with the trust domain printed in the error message (case-sensitive exact match)
  3. If the peer's trust domain was renamed, update its certs or the map key to the new domain name
  4. If the peer should not be trusted at all, block it at the network/load-balancer layer instead of adding its domain to the trust map

Example fix

// before: only local domain configured
Map<String, X509ExtendedTrustManager> delegates =
    Map.of("local-td", localTrustManager);

// after: include the federated domain from the error
Map<String, X509ExtendedTrustManager> delegates = Map.of(
    "local-td", localTrustManager,
    "remote-td", remoteTrustManager);
Defensive patterns

Strategy: validation

Validate before calling

// Check that every expected trust domain exists in the trust map before wiring delegates
static void requireTrustDomains(Map<String, X509ExtendedTrustManager> delegates,
    Collection<String> expectedDomains) {
  for (String d : expectedDomains) {
    if (!delegates.containsKey(d)) {
      throw new IllegalArgumentException("Trust map missing trust domain: " + d);
    }
  }
}

Type guard

static boolean trustDomainKnown(Map<String, X509ExtendedTrustManager> map, String spiffeUri) {
  int start = "spiffe://".length();
  int slash = spiffeUri.indexOf('/', start);
  return slash > start && map.containsKey(spiffeUri.substring(start, slash));
}

Try / catch

try {
  mtlsCall();
} catch (SSLHandshakeException e) {
  if (e.getCause() instanceof CertificateException
      && e.getCause().getMessage().contains("Spiffe Trust Map doesn't contain trust domain")) {
    String td = extractTrustDomain(e.getCause().getMessage());
    log.error("Add trust domain '" + td + "' to the SPIFFE trust map or reject the peer");
  }
  throw e;
}

Prevention

When it happens

Trigger: checkClientTrusted/checkServerTrusted is called with a peer whose SPIFFE URI SAN has trust domain X (e.g. spiffe://other-td/...) while the configured trust map only contains other domains; the error message names the offending trust domain.

Common situations: Cross-cluster/cross-org mTLS where the peer belongs to a trust domain not added to the local trust map; trust domain renamed during migration; typo in the trust map key (map keys must exactly match the SPIFFE URI's trust-domain component); federating a new environment without updating the mesh config.

Understand the failure class

Background: Record Not Found Errors: "not found", RecordNotFound, and "was not found" — what they mean and how to fix them — this error's family across 28 libraries.

Related errors


AI-assisted analysis of grpc/grpc-java@64daddc1f3 (2026-09-08). Data as JSON: /api/errors/03e8227c9dd9768f. Report an issue: GitHub.