1. Home
  2. Home
  3. Documentation
  4. Troubleshooting
  5. No suitable driver found
Last reviewed Edit this page on GitHub

No suitable driver found

The error, verbatim:

java.sql.SQLException: No suitable driver found for jdbc:postgresql://…

DriverManager.getConnection(url, …) walks every registered java.sql.Driver and asks each one to connect(url, …). The exception fires when every driver returns null. Either there are no pgJDBC drivers registered, or one is registered but does not recognise the URL you passed. The four sections below cover those cases in order of diagnostic value. DriverManager.getDriver(url) is slightly different: that lookup uses acceptsURL(url), but it is not the path that throws the longer No suitable driver found for ... message from getConnection.

Reviewed 2026-05-21 against source:Driver auto-registration:70-80, connect returns null for non-pgJDBC URLs:245-255, DriverManager registration test:483-522

Before touching configuration, dump the actual DriverManager state. This is the fastest way to distinguish “the JAR isn’t loaded” from “the JAR is loaded but the URL is wrong”:

import java.sql.DriverManager;
import java.util.Collections;

for (java.sql.Driver d : Collections.list(DriverManager.getDrivers())) {
    System.out.println(d.getClass().getName()
        + "  loaded by " + d.getClass().getClassLoader());
}

If org.postgresql.Driver does not appear in that list, jump to The JAR is not on the classpath or The shaded JAR lost its service-loader entry . If it does, jump to The URL does not match the driver’s pattern .

A common cause. DriverManager discovers drivers via the ServiceLoader mechanism: it reads files named META-INF/services/java.sql.Driver from every JAR on the classpath and loads the classes named there. If the postgresql JAR is not present, ServiceLoader finds nothing and the registration never happens.

Reviewed 2026-05-21 against source:ServiceLoader entry:1, Driver static initializer:70-80

Verify the dependency is actually on the build path:

# Maven
mvn dependency:tree | grep org.postgresql

# Gradle
./gradlew dependencies --configuration runtimeClasspath | grep org.postgresql

For a hand-rolled launch, the JAR must appear on -cp / -classpath:

java -cp postgresql-42.7.11.jar:myapp.jar com.example.Main

Class.forName("org.postgresql.Driver") does not rescue this case; the class still needs to be on the classpath to be loadable. See the install page’s ServiceLoader note : that call has been redundant since Java 6 and is not a substitute for a real dependency.

The shaded JAR lost its service-loader entry

Reviewed 2026-05-21 against source:ServiceLoader entry:1

When an application is repackaged into a single “fat” JAR (Maven Shade Plugin, Gradle Shadow, mvn-assembly, spring-boot:repackage), the service-loader file at META-INF/services/java.sql.Driver from each dependency must be merged into the final JAR. Many shading defaults silently drop these files when they collide across dependencies, which removes pgJDBC’s registration.

Symptoms specific to this case:

  • DriverManager.getDrivers() returns an empty (or partial) list at runtime even though org.postgresql.Driver.class.getResource(...) finds the class.
  • The app works when run from the IDE (raw classpath) but fails when launched from the shaded artifact.

Fix per build system:

Add the ServicesResourceTransformer:

<plugin>
  <artifactId>maven-shade-plugin</artifactId>
  <configuration>
    <transformers>
      <transformer implementation="org.apache.maven.plugins.shade.resource.ServicesResourceTransformer"/>
    </transformers>
  </configuration>
</plugin>

The plugin includes mergeServiceFiles() but does not call it by default:

tasks.shadowJar {
    mergeServiceFiles()
}

spring-boot-maven-plugin and spring-boot-gradle-plugin produce a nested fat JAR: dependencies stay as JARs inside BOOT-INF/lib/, the service-loader files are not collapsed, and nothing breaks. If you are seeing this error with a Spring Boot artifact, the build is producing a flat shaded JAR (custom packaging, or spring-boot:repackage was overridden); switch back to the default repackaging.

Reviewed 2026-05-21 against source:acceptsURL delegates to parseURL:451-463, parseURL prefix and URL grammar:535-688, accepted and rejected URL tests:72-175

org.postgresql.Driver.connect(url, …) returns null unless the URL starts with the literal prefix jdbc:postgresql:. Anything else is silently a “no” and falls through to the next driver, then to “No suitable driver found”. acceptsURL(url) uses the same parser, so it is still a useful standalone check.

Common typos and slips:

  • jdbc:postgres://... (missing the ql)
  • postgresql://... (missing the jdbc: scheme)
  • jdbc:postgresql//... (missing the : after postgresql)
  • An empty URL after a property-lookup mishap

The full URL grammar is documented at JDBC URL ; the simplest valid form is:

jdbc:postgresql://host:port/database

Reviewed 2026-05-21 against source:DriverManager registration:757-788, BaseDataSource loads Driver:62-77, BaseDataSource still uses DriverManager:101-115, OSGi activator registers driver and DataSourceFactory:26-68

In containers and modular runtimes (OSGi, JPMS, application servers, GraalVM polyglot), DriverManager.getConnection only sees drivers loaded by a classloader visible to the caller. A driver loaded by a child or sibling classloader can be registered yet still be skipped by DriverManager for that caller.

The DriverManager source documents this explicitly: it skips drivers whose Class is not visible from the caller’s classloader.

Workarounds, in decreasing order of cleanliness:

  • Move the JAR up to a classloader visible to the caller: the application classloader, the container’s shared classloader, the JPMS module’s transitive dependencies, the OSGi system bundle.
  • Use a DataSource instead of DriverManager. PGSimpleDataSource loads org.postgresql.Driver in BaseDataSource’s static initialiser before it asks DriverManager for a connection. That can fix ServiceLoader visibility problems, but the connection path still uses DriverManager, so the driver class must be visible to the code calling getConnection(). This is the recommended path for application servers anyway; see DataSource and JNDI .
  • Explicitly register from the caller’s classloader. Calling org.postgresql.Driver.register() (or Class.forName(...) on the driver class, loaded by your classloader) re-registers the driver under your classloader and makes it visible.
  • Quick start : the Maven / Gradle dependency declaration for the supported release lines.
  • JDBC URL : full JDBC URL grammar.
  • DataSource and JNDI : the PGSimpleDataSource path, including how it loads the pgJDBC driver before requesting a connection.