Error fix · Developer errors

Java PKIX path building failed: Fix the Truststore

Fix Java's "PKIX path building failed: unable to find valid certification path to requested target" with keytool, a custom truststore and the right JDK cacerts file.

Error messagePKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target

By SiteDownCheck Editorial · Updated October 14, 2026 · 4 min read

Java's TLS stack validates certificates using the PKIX algorithm against its own truststore, not against the operating system's. This exception means Java could not build a chain from the server's certificate to any root in that truststore:

javax.net.ssl.SSLHandshakeException: PKIX path building failed:
sun.security.provider.certpath.SunCertPathBuilderException:
unable to find valid certification path to requested target

The causes are the same as in other languages (missing intermediate, private CA, TLS-inspecting proxy, outdated roots), but the fix is Java specific because you change a keystore file or JVM flags.

Step 1: see what the server sends

openssl s_client -connect api.example.com:443 -servername api.example.com -showcerts </dev/null 2>/dev/null \
  | grep -E "^ *[0-9] s:|^ *i:|Verify return code"
  • If one certificate is sent and the verdict is code 20 or 21, the server omits its intermediate. Java will not download it for you. Fix the server: incomplete certificate chain.
  • If the issuer is a company or security-product name, a proxy is re-signing traffic. You need to trust that root.
  • If the chain looks normal and public, the JDK on that machine probably has an old or damaged cacerts.

Step 2: find which truststore this JVM uses

Without flags, Java uses lib/security/cacerts inside the JDK or JRE (in Java 8 it is jre/lib/security/cacerts). Different JDKs on one machine have different files, and a Docker image or an IDE may use another JDK from the one you tested.

java -XshowSettings:properties -version 2>&1 | grep -E "java.home|java.version"
ls "$JAVA_HOME/lib/security/cacerts"
keytool -list -cacerts | head      # Java 9+ shortcut; default password is changeit

Check for overrides in JAVA_TOOL_OPTIONS, JAVA_OPTS, or application startup flags containing -Djavax.net.ssl.trustStore. A custom truststore that holds only your private CA, with no public roots, causes this error for every public site.

Step 3: get and import the missing certificate

Export the CA (or the proxy root) as PEM, then import it:

sudo keytool -importcert -trustcacerts -alias corp-root \
  -file corp-root.pem \
  -keystore "$JAVA_HOME/lib/security/cacerts" \
  -storepass changeit

changeit is the conventional default password for the bundled cacerts; if your distribution changed it, use that one. Import the root or intermediate CA, not the server's leaf certificate, so renewals keep working.

Distribution JDKs may overwrite cacerts on upgrade. A safer approach for applications is a separate truststore:

keytool -importcert -alias corp-root -file corp-root.pem \
  -keystore /etc/myapp/truststore.p12 -storetype PKCS12 -storepass changeit
java -Djavax.net.ssl.trustStore=/etc/myapp/truststore.p12 \
     -Djavax.net.ssl.trustStorePassword=changeit \
     -Djavax.net.ssl.trustStoreType=PKCS12 -jar app.jar

Remember that setting trustStore replaces the default store for that JVM. To keep public roots, copy the default cacerts first and add to the copy.

Containers and build tools

  • Dockerfile: copy the PEM into the image and run keytool -importcert ... -cacerts during the build, or mount a truststore and pass the system properties.
  • Maven or Gradle failing to download: they run on a JVM too, so the build machine's cacerts needs the proxy root. Look at MAVEN_OPTS and GRADLE_OPTS.
  • Debian and Ubuntu: the ca-certificates-java package syncs the system store into the JVM store when you run sudo update-ca-certificates, so installing a PEM under /usr/local/share/ca-certificates/ with a .crt extension may be enough.

Debug the handshake

Turn on JSSE logging to see the certificates presented:

java -Djavax.net.debug=ssl:handshake:verbose -jar app.jar 2>&1 | grep -iE "certificate|issuer|alert"

The output is large; limit it to a short reproduction. You will see each certificate subject and issuer, which tells you where the chain breaks.

  • No subject alternative names matching IP address ... found or No subject alternative DNS name matching x found: hostname mismatch, covered in certificate name mismatch.
  • Received fatal alert: handshake_failure: protocol or cipher disagreement. See sslv3 alert handshake failure.
  • NotAfter in the debug output: an expired certificate in the chain.

Verify

Rerun the failing call and confirm it completes. List the imported entry with keytool -list -keystore ... -alias corp-root, and check the server from outside with the SSL Chain Debugger so you know whether other clients will hit the same problem.

Next steps

FAQ

Why does Java not use the operating system's certificates?

Historically the JDK ships and manages its own cacerts file. Some distributions link it to the system store, but you should not assume it.

What is the password for cacerts?

The default for the bundled file is changeit. Treat it as a file-integrity password, not a secret, and check your distribution if it fails.

Should I import the server certificate or the CA?

Import the CA (root or intermediate). A leaf certificate expires with each renewal, so importing it needs repeating.

It works in my browser. Why not Java?

Browsers fetch missing intermediates and use the OS store. Java does neither, so a server with an incomplete chain breaks Java alone.

Get notified when this fix updates

Plus occasional practical tips on uptime and monitoring, nothing else. Unsubscribe anytime.

This page is general information written by our editorial team and updated when documentation changes. Servers and providers differ, so test changes safely first. See our editorial policy and disclaimer.