Problem
A build, a dependency download, or a program started from the IDE stops with a TLS error:
javax.net.ssl.SSLHandshakeException: (certificate_unknown) PKIX path building failed:
sun.security.provider.certpath.SunCertPathBuilderException:
unable to find valid certification path to requested target
The same failure appears from any JVM tool that the IDE starts:
- Maven import or goal execution, wrapped in
Could not transfer artifact ... from/to <repository> - Gradle sync or a Gradle task, wrapped in
Could not GET ...orCould not resolve ... - Bazel fetching an external repository over HTTPS
- A Java, Kotlin, Scala, or Groovy run configuration that opens an HTTPS connection
- Ant, sbt, or any other JVM process launched from the IDE
The same URL opens in a browser with no warning, because the operating system trusts the certificate authority and the JDK does not.
Cause
The IDE starts Maven, Gradle, Bazel, and run configurations as separate operating system processes. Each one runs in its own JVM, and each JVM validates TLS certificates against its own truststore, the file lib/security/cacerts inside that JDK.
A JDK ships with public root certificates only. Three setups present a certificate that those roots cannot validate:
- an internal repository such as Nexus or Artifactory, signed by a company certificate authority
- a proxy or firewall that decrypts TLS traffic and re-signs it with its own authority
- a self-signed certificate on an internal server
A JDK that the IDE downloaded fails most often, because nothing is ever added to it after the download. Those JDKs go to ~/.jdks on macOS and Linux, and to C:\Users\user\.jdks on Windows.
The IDE trust store covers a different set of processes
The IDE has its own certificate store at Settings | Appearance & Behavior | System Settings | Server Certificates. It does not extend to every process the IDE starts, so an imported certificate can look correct and still have no effect.
The store applies to connections that the IDE process makes itself: JetBrains services, the plugin repository, the HTTP Client, and HTTPS version control access from the IDE.
It also applies to Maven project import and dependency resolution, since IntelliJ IDEA 2024.3 (Maven Server SSL should use standard Idea certificate managers). The Maven server process passes an unknown certificate back to the IDE, and the IDE shows the Untrusted Server Certificate prompt. Accepting that prompt covers one request. The certificate is not added to the store, so the next import asks again. The IDE registry key maven.delegate.trust.ssl.to.ide controls this delegation and is enabled by default. The delegation also turns itself off when the system property javax.net.ssl.keyStore is set, because that setting indicates client certificate authentication.
The store does not apply to Maven goals, Gradle, Bazel, run configurations, or anything started from the terminal. Maven import is the only exception, and it is implemented in the Maven server alone. For every other tool, the JDK truststore is the only one that is read.
The Maven importer inherits SSL properties from the IDE
Every system property of the IDE process whose name starts with javax.net.ssl is copied into the Maven server process. A -Djavax.net.ssl.trustStore line in the IDE .vmoptions file therefore reaches the Maven importer without any Maven setting. That copy does not reach Maven goals, Gradle, Bazel, or run configurations, which are started separately.
Resolution
Step 1. Find the JDK that the failing tool uses
| Tool | Where the JDK is selected |
|---|---|
| Maven import | Settings | Build, Execution, Deployment | Build Tools | Maven | Importing, field JDK for importer |
| Maven goals | Settings | Build, Execution, Deployment | Build Tools | Maven | Runner, field JRE |
| Gradle | Settings | Build, Execution, Deployment | Build Tools | Gradle, field Gradle JVM |
| Run configuration | The run/debug configuration itself, field JRE |
| Bazel | The Bazel server JVM, which the IDE does not select |
A field set to Use Project JDK resolves to the project SDK. Several tools often point at the same JDK. If they do, one import fixes all of them.
Bazel is the exception. Use the separate truststore file described below, which works without knowing which JDK the Bazel server runs.
Step 2. Get the CA certificate
The certificate from the IT department is the reliable source, because the export below can miss the root. Ask for a Base-64 encoded .cer file.
To export it without waiting, print the chain that the server sends. keytool comes with every JDK:
keytool -printcert -sslserver repo.example.com:443 -rfc
openssl prints the same chain:
openssl s_client -showcerts -connect repo.example.com:443 </dev/null
Both print one PEM block per certificate. Copy the last block, including its BEGIN CERTIFICATE and END CERTIFICATE lines, into a file such as company-root.cer.
A server does not always send its root certificate. If the printed chain ends at an intermediate certificate, ask the IT department for the root, or export it from the browser: open the URL, click the padlock, open the certificate details, select the top entry of the certification path, and save it as Base-64 encoded X.509 (.CER).
Step 3. Import the certificate into the JDK truststore
Run keytool from the bin directory of the JDK found in step 1:
keytool -importcert -trustcacerts -cacerts -storepass changeit -alias company-root -file company-root.cer
-cacerts selects the truststore of the JDK that owns the keytool command, so the path does not have to be typed. That option needs JDK 9 or later. On JDK 8, name the file instead:
keytool -importcert -trustcacerts -keystore <jdk>/jre/lib/security/cacerts -storepass changeit -alias company-root -file company-root.cer
Answer yes at the trust prompt. If the chain contains more than one company certificate, import each one under a different alias. If keytool reports that the alias exists, choose another name.
Restart the IDE afterwards. For Gradle, also stop the daemon with gradlew --stop. A JVM reads the truststore at startup, so a long-running daemon keeps the old list.
The import is per JDK, and a JDK upgrade installs a new cacerts. Repeat the import after every JDK change, or use the separate truststore file described below.
Step 4. Clear the cached failure
Maven and Gradle remember a failed download and keep reporting the old error after a correct fix.
- Maven: delete the affected directory under
~/.m2/repository, or enable Always update snapshots in Settings | Build, Execution, Deployment | Build Tools | Maven. - Gradle: run
gradlew --stop, then re-sync the project.
To confirm that the import landed, list the alias:
keytool -list -cacerts -storepass changeit -alias company-root
Alternative: use the Windows certificate store
On Windows, one VM option makes the JVM read the Windows root store instead of its own list. No certificate file is needed, because a managed machine already carries the company authority there:
-Djavax.net.ssl.trustStoreType=WINDOWS-ROOT
Open the failing URL in Edge or Chrome first. If the browser shows no warning, Windows trusts the certificate and this option works.
The option replaces the JDK list rather than adding to it. Measured on a Windows build of Java 21, the trust anchor count changed from 146 in the JDK list to 124 in the Windows list. A managed machine carries the public roots in the Windows store as well, so the replacement is safe there. On an unmanaged machine, compare the two lists before relying on it.
Alternative: a separate truststore file
Copy cacerts, import the company certificate into the copy, and point the tool at that copy. The fix then stays outside the JDK, survives a JDK upgrade, and can be committed for a team:
-Djavax.net.ssl.trustStore=/path/to/company-truststore.jks
-Djavax.net.ssl.trustStorePassword=changeit
This is the option to use for Bazel.
Where to put a VM option
Both alternatives above are VM options, and they go in the same places.
| Tool | Location |
|---|---|
| Maven import | VM options for importer in Maven | Importing |
| Maven goals | VM Options in Maven | Runner |
| Maven, whole project |
.mvn/jvm.config in the project root, one option per line |
| Gradle |
gradle.properties, with the systemProp. prefix that passes a value to the daemon JVM: systemProp.javax.net.ssl.trustStoreType=WINDOWS-ROOT
|
| Run configuration | The VM options field of the run/debug configuration |
| Bazel |
.bazelrc, as a startup option: startup --host_jvm_args=-Djavax.net.ssl.trustStore=/path/to/company-truststore.jks
|
| Maven import, through the IDE | The IDE .vmoptions file, reachable from Help | Edit Custom VM Options. Only javax.net.ssl properties propagate, and only to the Maven importer. |
.mvn/jvm.config covers both Maven roles, and the two IDE fields do not add to it. Each field replaces the file for its own role: a non-empty VM options for importer makes the importer ignore .mvn/jvm.config, and a non-empty Runner VM Options does the same for goal execution. Leave a field empty to let the file apply.
What not to do
Do not turn off certificate validation, which removes the protection against a forged server. These settings belong in a single diagnostic run and nowhere else:
- Maven:
-Dmaven.wagon.http.ssl.insecure=true -Dmaven.wagon.http.ssl.allowall=true - Gradle:
systemProp.http.sslVerify=falseingradle.properties
A build that succeeds with them confirms the certificate as the only cause. Remove them immediately afterwards.
If the error remains
- Check which certificate the server actually sends, with the
keytool -printcert -sslservercommand from step 2. A TLS-inspecting proxy replaces the server certificate, and then the authority to import is the proxy authority, not the one the server owns. - Check the proxy configuration. Maven reads proxy settings from
~/.m2/settings.xmland Gradle fromgradle.properties. The IDE setting at Settings | Appearance & Behavior | System Settings | HTTP Proxy applies to the IDE process and does not reach these tools. - Check that the JDK selected in step 1 is the JDK that was modified in step 3. A field left at Use Project JDK follows the project SDK, which can change.
Related articles
- How to troubleshoot "Untrusted Server Certificate Warning" covers the IDE certificate store itself and the prompt it shows.
- Gradle "PKIX path building failed" with duplicate library versions covers the dependency conflicts that follow an incomplete Gradle resolution.
- Claude Agent does not work with SSL traffic decryption (PKIX path building failed) applies the same external-process rule to a non-JVM tool, which needs its own certificate authority configuration.
- Code With Me On-Premises: unable to download JetBrains Client covers the truststore of the lobby server.
- Connection fails with 'PKIX path building failed: unable to find valid certification path to requested target' error covers database connections. Those are configured on the data source rather than on a JDK, so the steps above do not apply to them.
Comments
0 comments
Please sign in to leave a comment.