{"record":{"id":"173394024ac1989d","repo":"grpc/grpc-java","slug":"failed-to-get-client-tls-configuration-from-s2a","errorCode":null,"errorMessage":"Failed to get client TLS configuration from S2A.","messagePattern":"Failed to get client TLS configuration from S2A\\.","errorType":"exception","errorClass":"GeneralSecurityException","httpStatus":null,"severity":"critical","filePath":"s2a/src/main/java/io/grpc/s2a/internal/handshaker/SslContextFactory.java","lineNumber":79,"sourceCode":"   * @throws IOException if an unexpected response from S2A server is received.\n   * @throws InterruptedException if {@code stub} is closed.\n   */\n  static SslContext createForClient(\n      S2AStub stub, String targetName, Optional<S2AIdentity> localIdentity)\n      throws IOException,\n          InterruptedException,\n          CertificateException,\n          KeyStoreException,\n          NoSuchAlgorithmException,\n          UnrecoverableKeyException,\n          GeneralSecurityException {\n    checkNotNull(stub, \"stub should not be null.\");\n    checkNotNull(targetName, \"targetName should not be null on client side.\");\n    GetTlsConfigurationResp.ClientTlsConfiguration clientTlsConfiguration;\n    try {\n      clientTlsConfiguration = getClientTlsConfigurationFromS2A(stub, localIdentity);\n    } catch (IOException | InterruptedException e) {\n      throw new GeneralSecurityException(\"Failed to get client TLS configuration from S2A.\", e);\n    }\n\n    // Use the default value for timeout.\n    // Use the smallest possible value for cache size.\n    // The Provider is by default OPENSSL. No need to manually set it.\n    SslContextBuilder sslContextBuilder =\n        GrpcSslContexts.configure(SslContextBuilder.forClient())\n            .sessionCacheSize(1)\n            .sessionTimeout(0);\n\n    configureSslContextWithClientTlsConfiguration(clientTlsConfiguration, sslContextBuilder);\n    sslContextBuilder.trustManager(\n        S2ATrustManager.createForClient(stub, targetName, localIdentity));\n    sslContextBuilder.option(\n        OpenSslContextOption.PRIVATE_KEY_METHOD, S2APrivateKeyMethod.create(stub, localIdentity));\n    sslContextBuilder.option(\n        OpenSslContextOption.GROUPS,\n        new String[] {\"X25519MLKEM768\", \"x25519\", \"secp256r1\", \"secp384r1\", \"secp521r1\"});","sourceCodeStart":61,"sourceCodeEnd":97,"githubUrl":"https://github.com/grpc/grpc-java/blob/64daddc1f3d1975670f769f3e97bde8b2ba32d25/s2a/src/main/java/io/grpc/s2a/internal/handshaker/SslContextFactory.java#L61-L97","documentation":"SslContextFactory.createForClient() fetches the client TLS configuration from the S2A and wraps any IOException or InterruptedException from getClientTlsConfigurationFromS2A() in a GeneralSecurityException 'Failed to get client TLS configuration from S2A.'. The client cannot build its SslContext without this configuration, so the failure is fatal to client setup.","triggerScenarios":"Calling SslContextFactory.createForClient(stub, targetName, localIdentity) where the underlying S2AStub stream send/receive throws IOException or InterruptedException — S2A down, stream closed, or thread interrupted during configuration fetch.","commonSituations":"Client bootstrapping mTLS with S2A while the S2A process is not running; wrong S2A address in client config; reusing a closed stub; shutdown interrupting configuration retrieval.","solutions":["Start/verify the S2A process and confirm the client's configured address reaches it.","Create a fresh S2AStub before building the SslContext if a previous stub's stream closed.","Retry createForClient() with backoff for transient connectivity issues during startup.","Check the cause chain ( getTargetException/getCause ) to distinguish IOException from InterruptedException."],"exampleFix":"// before\nSslContext ctx = SslContextFactory.createForClient(stub, targetName, identity); // fails if S2A down\n// after\nwaitForS2aReady(channel, Duration.ofSeconds(10));\nSslContext ctx = SslContextFactory.createForClient(stub, targetName, identity);","handlingStrategy":"try-catch","validationCode":"// verify the S2A is ready before building the SslContext\nif (!channel.awaitTerminationCheckOrReady(channel.getState(false), 10, TimeUnit.SECONDS)) {\n  throw new IllegalStateException(\"S2A channel not ready\");\n}","typeGuard":null,"tryCatchPattern":"try {\n  SslContext ctx = SslContextFactory.createForClient(stub, targetName, identity);\n} catch (GeneralSecurityException e) {\n  // retry with a fresh stub for transient S2A availability issues\n  SslContext ctx = SslContextFactory.createForClient(newStubFor(channel), targetName, identity);\n}","preventionTips":["Wait for S2A readiness before client bootstrap.","Create a fresh stub for each SslContext build, or rebuild on failure.","Retry with backoff during startup when the S2A may still be initializing.","Check the wrapped cause to distinguish availability issues from interrupts."],"tags":["tls","client-config","s2a","grpc","network"],"backgroundTag":"missing-configuration","analyzedSha":"64daddc1f3d1975670f769f3e97bde8b2ba32d25","analyzedAt":"2026-09-08T06:14:57.704Z","contentChangedAt":"2026-09-08T06:14:57.704Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}