{"record":{"id":"a3e21de67e2c1355","repo":"alibaba/canal","slug":"can-t-verify-server-certificate-because-no-trust-m","errorCode":null,"errorMessage":"Can't verify server certificate because no trust manager is found.","messagePattern":"Can't verify server certificate because no trust manager is found\\.","errorType":"exception","errorClass":"CertificateException","httpStatus":null,"severity":"critical","filePath":"driver/src/main/java/com/alibaba/otter/canal/parse/driver/mysql/socket/BioSocketChannelPool.java","lineNumber":280,"sourceCode":"                try {\n                    CertPath certPath = this.certFactory.generateCertPath(Arrays.asList(chain));\n                    // Validate against truststore\n                    CertPathValidatorResult result = this.validator.validate(certPath, this.validatorParams);\n                    // Check expiration for the CA used to validate this path\n                    ((PKIXCertPathValidatorResult) result).getTrustAnchor().getTrustedCert().checkValidity();\n\n                } catch (InvalidAlgorithmParameterException e) {\n                    throw new CertificateException(e);\n                } catch (CertPathValidatorException e) {\n                    throw new CertificateException(e);\n                }\n            }\n\n            if (this.verifyServerCert) {\n                if (this.origTm != null) {\n                    this.origTm.checkServerTrusted(chain, authType);\n                } else {\n                    throw new CertificateException(\n                        \"Can't verify server certificate because no trust manager is found.\");\n                }\n\n                // verify server certificate identity\n                if (this.hostName != null) {\n                    logger.info(\"verify hostName: {}\", this.hostName);\n                    Set<String> expectHostNames = new HashSet<>();\n                    for (X509Certificate certificate : chain) {\n                        String dn = certificate.getSubjectX500Principal().getName(X500Principal.RFC2253);\n                        String cn = null;\n                        try {\n                            LdapName ldapDN = new LdapName(dn);\n                            for (Rdn rdn : ldapDN.getRdns()) {\n                                if (rdn.getType().equalsIgnoreCase(\"CN\")) {\n                                    cn = rdn.getValue().toString();\n                                    break;\n                                }\n                            }","sourceCodeStart":262,"sourceCodeEnd":298,"githubUrl":"https://github.com/alibaba/canal/blob/87be50e87686a3e8af08c368d0e1ffd1f59eb04a/driver/src/main/java/com/alibaba/otter/canal/parse/driver/mysql/socket/BioSocketChannelPool.java#L262-L298","documentation":"Thrown inside the X509TrustManagerWrapper.checkServerTrusted when verifyServerCert is true (VERIFY_CA/VERIFY_IDENTITY) but origTm is null, i.e. no backing X509TrustManager was supplied. The wrapper cannot validate the server certificate chain against any trust store, so it refuses to trust. This is the 'I was asked to verify but have nothing to verify against' failure.","triggerScenarios":"Constructing an X509TrustManagerWrapper with a null origTm (the verifyServerCert-only constructor) and then calling checkServerTrusted; or the trust-manager list built in getSSLContext having no X509TrustManager to delegate to. Triggered during SSL handshake when VERIFY_CA/VERIFY_IDENTITY is set without a usable trust store.","commonSituations":"VERIFY_CA/VERIFY_IDENTITY configured but no trustCertificateKeyStoreUrl supplied and the default JVM truststore (cacerts) is empty/missing; the fallback wrapper path was taken (tms.size()==0 branch) and verification was requested; misconfigured truststore URL/type/password that produced no X509TrustManager.","solutions":["Provide a valid trustCertificateKeyStoreUrl (and type/password) pointing to a JKS/PKCS12 store containing the MySQL server's CA.","Ensure the JVM default cacerts is present and populated when relying on fallbackToDefaultTrustStore.","Verify the truststore URL is reachable and the type matches the file format (JKS vs PKCS12).","If you intentionally cannot verify, use SslMode.REQUIRED instead of VERIFY_CA/VERIFY_IDENTITY."],"exampleFix":"// before\nSslInfo info = new SslInfo();\ninfo.setSslMode(SslMode.VERIFY_CA);\n// no trust store set -> handshake throws\n\n// after\ninfo.setTrustCertificateKeyStoreUrl(\"file:/etc/canal/truststore.jks\");\ninfo.setTrustCertificateKeyStoreType(\"JKS\");\ninfo.setTrustCertificateKeyStorePassword(\"changeit\");","handlingStrategy":"validation","validationCode":"boolean verify = mode == SslMode.VERIFY_CA || mode == SslMode.VERIFY_IDENTITY;\nboolean hasTrustStore = StringUtils.isNotEmpty(sslInfo.getTrustCertificateKeyStoreUrl());\nif (verify && !hasTrustStore) {\n    // ensure default cacerts exists, else fail fast with a clear message\n}","typeGuard":"public static boolean canVerify(SslInfo info) {\n    SslMode m = info.getSslMode();\n    if (m != SslMode.VERIFY_CA && m != SslMode.VERIFY_IDENTITY) return true;\n    return StringUtils.isNotEmpty(info.getTrustCertificateKeyStoreUrl())\n        || defaultCacertsExists();\n}","tryCatchPattern":"try {\n    socketFactory.createSocket(...).startHandshake();\n} catch (javax.net.ssl.SSLHandshakeException e) {\n    if (e.getMessage().contains(\"no trust manager\")) {\n        // configure trustCertificateKeyStoreUrl or use REQUIRED\n    }\n    throw e;\n}","preventionTips":["Supply a valid trustCertificateKeyStoreUrl for VERIFY_CA/VERIFY_IDENTITY.","Confirm the JVM default cacerts is present when relying on fallback.","Use SslMode.REQUIRED when you cannot supply a trust store."],"tags":["network","ssl","tls","certificate","truststore","security"],"backgroundTag":null,"analyzedSha":"87be50e87686a3e8af08c368d0e1ffd1f59eb04a","analyzedAt":"2026-08-14T04:30:11.918Z","schemaVersion":2},"datasetVersion":"2026-08-14T05:17:29.042Z"}