Testcontainers 2.0 NoSuchFieldError: fix old jackson-annotations

You bump Testcontainers to 2.0 to get Docker 29 support, and your integration tests now fail before a single container starts. The log shows java.lang.NoSuchFieldError: READ_UNKNOWN_ENUM_VALUES_USING_DEFAULT_VALUE. On JDK 21 the same error is printed in a longer form: Class com.fasterxml.jackson.annotation.JsonFormat$Feature does not have member field ....

Testcontainers 2.0.5 shades jackson-databind 2.18.4, but it loads jackson-annotations from your classpath.
If your project pulls in jackson-annotations 2.14 or older, JsonFormat$Feature lacks the field and you get NoSuchFieldError. The field exists from 2.15.0.
Pin only jackson-annotations to 2.20 with dependencyManagement. jackson-databind can stay on 2.12.7.

Environment

I reproduced this with Maven tests against Docker Desktop on Windows. To see the JDK 17 message format, I ran the same project inside the maven:3.10.0-eclipse-temurin-17 container.

Item Version
JDK 21.0.12.1 (Temurin), 17.0.20.1 for comparison (Docker image)
Maven 3.10.0
Docker Desktop Engine 29.9.2, API 1.56 (minimum 1.40)
Testcontainers 2.0.5 (reproduce and fix), 1.21.3 and 1.21.4 (comparison)
jackson-databind (application) 2.12.7 (2.13.5, 2.14.3, 2.15.4 for comparison)
jackson-annotations (after the fix) 2.20
JUnit 5.14.4

Reproduce: Testcontainers 2.0.5 throws NoSuchFieldError before the container starts

The setup is a project whose Jackson version is locked to 2.12 by its runtime platform or a shared library. The application uses jackson-databind 2.12.7 directly, and Testcontainers 2.0.5 is added only in test scope.

<properties>
    <testcontainers.version>2.0.5</testcontainers.version>
    <!-- Jackson version fixed by the runtime platform (middleware) -->
    <jackson.version>2.12.7</jackson.version>
</properties>

<dependencies>
    <!-- application dependency pinned to Jackson 2.12 -->
    <dependency>
        <groupId>com.fasterxml.jackson.core</groupId>
        <artifactId>jackson-databind</artifactId>
        <version>${jackson.version}</version>
    </dependency>

    <dependency>
        <groupId>org.testcontainers</groupId>
        <artifactId>testcontainers</artifactId>
        <version>${testcontainers.version}</version>
        <scope>test</scope>
    </dependency>
    ...
</dependencies>

The test only starts one alpine container. Before that, it prints where JsonFormat.Feature was loaded from, so you can see which jackson-annotations jar is actually in use.

@Test
void startsAlpineContainer() {
    // which jackson-annotations jar is on the test classpath
    System.out.println("jackson-annotations jar: "
            + JsonFormat.Feature.class.getProtectionDomain().getCodeSource().getLocation());

    try (GenericContainer<?> alpine = new GenericContainer<>(DockerImageName.parse("alpine:3.24.3"))
            .withCommand("sleep", "30")) {
        alpine.start();
        assertTrue(alpine.isRunning());
    }
}

Running mvn test fails before any container is created.

jackson-annotations jar: file:/home/user/project/.tools/m2/repository/com/fasterxml/jackson/core/jackson-annotations/2.12.7/jackson-annotations-2.12.7.jar
[main] INFO org.testcontainers.DockerClientFactory - Testcontainers version: 2.0.5
...
java.lang.NoSuchFieldError: Class com.fasterxml.jackson.annotation.JsonFormat$Feature does not have member field 'com.fasterxml.jackson.annotation.JsonFormat$Feature READ_UNKNOWN_ENUM_VALUES_USING_DEFAULT_VALUE'
	at org.testcontainers.shaded.com.fasterxml.jackson.databind.deser.std.EnumDeserializer.createContextual(EnumDeserializer.java:235)
	...
	at org.testcontainers.shaded.com.github.dockerjava.core.exec.InfoCmdExec.exec(InfoCmdExec.java:24)
	...
	at org.testcontainers.dockerclient.DockerClientProviderStrategy.tryOutStrategy(DockerClientProviderStrategy.java:289)
	...
	at org.testcontainers.containers.GenericContainer.start(GenericContainer.java:316)

Read the stack from the bottom up. GenericContainer.start() prepares a Docker client and asks Docker for its info through InfoCmdExec. The shaded EnumDeserializer then parses that JSON response and can’t find a field on JsonFormat$Feature.

On JDK 17 the message is shorter. This is the format reported in GitHub issue #11236.

[main] INFO org.testcontainers.DockerClientFactory - Testcontainers version: 2.0.5
[ERROR] Tests run: 1, Failures: 0, Errors: 1, Skipped: 0, Time elapsed: 2.148 s <<< FAILURE! -- in com.example.tcjackson.AlpineContainerTest
[ERROR] com.example.tcjackson.AlpineContainerTest.startsAlpineContainer -- Time elapsed: 2.099 s <<< ERROR!
java.lang.NoSuchFieldError: READ_UNKNOWN_ENUM_VALUES_USING_DEFAULT_VALUE
	at org.testcontainers.shaded.com.fasterxml.jackson.databind.deser.std.EnumDeserializer.createContextual(EnumDeserializer.java:235)

Notice what’s missing: there is no Could not find a valid Docker environment line. Nothing in the log points to a Docker connection problem. The test just ends with NoSuchFieldError. In the same run, the application-side AppJacksonTest passed on jackson-databind 2.12.7.

Cause: shaded jackson-databind 2.18.4 reads jackson-annotations from your classpath

Testcontainers relocates its internal dependencies into the org.testcontainers.shaded package and bundles them in its jar. That’s why its jackson-databind doesn’t clash with your project’s version. The catch is that jackson-annotations isn’t part of that shading.

I checked the contents of testcontainers-2.0.5.jar and the constant in the shaded PackageVersion class.

$ unzip -l .tools/m2/repository/org/testcontainers/testcontainers/2.0.5/testcontainers-2.0.5.jar | grep -c 'org/testcontainers/shaded/com/fasterxml/jackson/databind/'
812
$ unzip -l .tools/m2/repository/org/testcontainers/testcontainers/2.0.5/testcontainers-2.0.5.jar | grep -c 'com/fasterxml/jackson/annotation/'
0
$ javap -v -cp .tools/m2/repository/org/testcontainers/testcontainers/2.0.5/testcontainers-2.0.5.jar org.testcontainers.shaded.com.fasterxml.jackson.databind.cfg.PackageVersion | grep 'String 2\.'
         0: ldc           #22                 // String 2.18.4

The jar has 812 shaded databind entries and zero annotation classes. The shaded databind is version 2.18.4. It still looks up com.fasterxml.jackson.annotation.JsonFormat$Feature under the original package name, so the JVM loads whatever jackson-annotations sits on your classpath.

Which jackson-annotations version added the field

I compared the jars directly with javap.

$ javap -cp .tools/m2/repository/com/fasterxml/jackson/core/jackson-annotations/2.12.7/jackson-annotations-2.12.7.jar 'com.fasterxml.jackson.annotation.JsonFormat$Feature' | grep READ_UNKNOWN
(exit=1)
$ javap -cp .tools/m2/repository/com/fasterxml/jackson/core/jackson-annotations/2.14.3/jackson-annotations-2.14.3.jar 'com.fasterxml.jackson.annotation.JsonFormat$Feature' | grep READ_UNKNOWN
(exit=1)
$ javap -cp .tools/m2/repository/com/fasterxml/jackson/core/jackson-annotations/2.15.0/jackson-annotations-2.15.0.jar 'com.fasterxml.jackson.annotation.JsonFormat$Feature' | grep READ_UNKNOWN
  public static final com.fasterxml.jackson.annotation.JsonFormat$Feature READ_UNKNOWN_ENUM_VALUES_AS_NULL;
  public static final com.fasterxml.jackson.annotation.JsonFormat$Feature READ_UNKNOWN_ENUM_VALUES_USING_DEFAULT_VALUE;

2.12.7 and 2.14.3 don’t have the field, and 2.15.0 does. The Javadoc in the jackson-annotations source also marks both constants with @since 2.15. The body of issue #11236 says you need something "> 2.12", but comments on the issue report the same error on 2.13.5 and 2.14.1. The real baseline is 2.15.

To confirm this end to end, I ran the same test and changed only -Djackson.version. When jackson-databind changes, jackson-annotations follows it to the same version.

jackson-annotations on the classpath Testcontainers 2.0.5 result
2.12.7 NoSuchFieldError
2.13.5 NoSuchFieldError
2.14.3 NoSuchFieldError
2.15.4 BUILD SUCCESS
2.20 (dependencyManagement) BUILD SUCCESS

Find the old jackson-annotations with mvn dependency:tree -Dverbose

Testcontainers 2.0.5 depends on docker-java-api 3.7.1, which declares jackson-annotations 2.20. So why did 2.12.7 end up on the classpath? The dependency tree answers that. With -Dverbose, Maven also prints the versions it dropped.

mvn org.apache.maven.plugins:maven-dependency-plugin:3.11.0:tree -Dverbose -Dincludes=com.fasterxml.jackson.core
[INFO] com.example.tcjackson:before:jar:0.0.1-SNAPSHOT
[INFO] +- com.fasterxml.jackson.core:jackson-databind:jar:2.12.7:compile
[INFO] |  +- com.fasterxml.jackson.core:jackson-annotations:jar:2.12.7:compile
[INFO] |  \- com.fasterxml.jackson.core:jackson-core:jar:2.12.7:compile
[INFO] \- org.testcontainers:testcontainers:jar:2.0.5:test
[INFO]    \- com.github.docker-java:docker-java-api:jar:3.7.1:test
[INFO]       \- (com.fasterxml.jackson.core:jackson-annotations:jar:2.20:test - omitted for conflict with 2.12.7)

The last line, omitted for conflict with 2.12.7, is the cause. When two versions of the same artifact meet, Maven picks the one nearest to your project in the tree. 2.12.7 sits at depth 2, while 2.20 sits at depth 3, behind testcontainers and docker-java-api. The nearest version wins, not the highest.

In a real project, the dependency that drags in the old version might not be jackson-databind. It could be a middleware client, a shared internal module, or an old BOM. Filtering the tree to the Jackson group with -Dincludes shows exactly which line brings in 2.14 or older.

Fix: pin jackson-annotations to 2.20 with dependencyManagement

If jackson-databind has to stay on 2.12.7, upgrade only jackson-annotations. An entry in dependencyManagement applies that version wherever the artifact appears in the tree. I matched it to 2.20, the version docker-java-api 3.7.1 uses.

<properties>
    ...
    <jackson.version>2.12.7</jackson.version>
    <!-- same version docker-java-api 3.7.1 (Testcontainers 2.0.5) depends on -->
    <jackson-annotations.version>2.20</jackson-annotations.version>
</properties>

<dependencyManagement>
    <dependencies>
        <!-- override only jackson-annotations; jackson-databind stays 2.12.7 -->
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-annotations</artifactId>
            <version>${jackson-annotations.version}</version>
        </dependency>
    </dependencies>
</dependencyManagement>

Run the tree again. Only jackson-annotations moves to 2.20, and jackson-databind and jackson-core stay on 2.12.7.

[INFO] +- com.fasterxml.jackson.core:jackson-databind:jar:2.12.7:compile
[INFO] |  +- com.fasterxml.jackson.core:jackson-annotations:jar:2.20:compile (version managed from 2.12.7)
[INFO] |  \- com.fasterxml.jackson.core:jackson-core:jar:2.12.7:compile
[INFO] \- org.testcontainers:testcontainers:jar:2.0.5:test
[INFO]    \- com.github.docker-java:docker-java-api:jar:3.7.1:test
[INFO]       \- (com.fasterxml.jackson.core:jackson-annotations:jar:2.20:test - version managed from 2.20; omitted for duplicate)

Now the test connects to Docker and the alpine container starts.

jackson-annotations jar: file:/home/user/project/.tools/m2/repository/com/fasterxml/jackson/core/jackson-annotations/2.20/jackson-annotations-2.20.jar
[main] INFO org.testcontainers.DockerClientFactory - Testcontainers version: 2.0.5
...
[main] INFO org.testcontainers.DockerClientFactory - Connected to docker: 
  Server Version: 29.9.2
  API Version: 1.56
...
[main] INFO tc.alpine:3.24.3 - Container alpine:3.24.3 started in PT0.2364997S
[INFO] Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 1.735 s -- in com.example.tcjackson.AlpineContainerTest
jackson-databind version: 2.12.7
[INFO] Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.093 s -- in com.example.tcjackson.AppJacksonTest
[INFO] Tests run: 2, Failures: 0, Errors: 0, Skipped: 0

The application-side AppJacksonTest also passed, serializing with an ObjectMapper from jackson-databind 2.12.7. That test only serializes a single Map, though. In a real project, run your existing JSON tests as well before you settle on this mix of versions.

Pitfall: the pinned version has compile scope

Look at the scope of jackson-annotations 2.20 in the tree: it’s compile. That means 2.20 also ends up in your packaged artifact, not only in tests. If your application server or middleware ships its own jackson-annotations, which jar gets loaded after deployment depends on that server’s configuration.

When you can’t upgrade: 1.21.4 passes, 1.21.3 fails on Docker 29

If you can’t change jackson-annotations at all, you can stay on Testcontainers 1.x. Comments on issue #11236 mention downgrading to 1.x as a workaround. In the same before project, I kept jackson-annotations 2.12.7 and changed only the Testcontainers version.

mvn -Dtestcontainers.version=1.21.4 test

1.21.4 finished with BUILD SUCCESS. The reason is the shaded jackson-databind version inside the 1.21.4 jar.

$ unzip -l .tools/m2/repository/org/testcontainers/testcontainers/1.21.4/testcontainers-1.21.4.jar | grep -c 'com/fasterxml/jackson/annotation/'
0
$ javap -v -cp .tools/m2/repository/org/testcontainers/testcontainers/1.21.4/testcontainers-1.21.4.jar org.testcontainers.shaded.com.fasterxml.jackson.databind.cfg.PackageVersion | grep 'String 2\.'
         0: ldc           #22                 // String 2.8.8

1.21.4 doesn’t shade the annotations either. But its internal databind is 2.8.8, so it never looks for a field added in 2.15. In 2.0.5 the internal databind is 2.18.4, and that’s what raises the requirement on the jackson-annotations jar from your classpath to 2.15 or newer.

1.21.3, under the same conditions, failed for a different reason.

[main] INFO org.testcontainers.DockerClientFactory - Testcontainers version: 1.21.3
...
[main] ERROR org.testcontainers.dockerclient.DockerClientProviderStrategy - Could not find a valid Docker environment. Please check configuration. Attempted configurations were:
	NpipeSocketClientProviderStrategy: failed with exception BadRequestException (Status 400: {"ID":"","Containers":0,"ContainersRunning":0,...
...
java.lang.IllegalStateException: Could not find a valid Docker environment. Please see logs and check configuration

Docker 29 engines reject the default API version that 1.21.3 requests. Issue #11235 reports the same HTTP 400 with 1.21.3 on Docker 29. The 1.21.4 release notes say: "This release makes version 1.21.x works with recent Docker Engine changes."

Option jackson-annotations Result on Docker 29.9.2 Verdict
2.0.5 as is 2.12.7 NoSuchFieldError Doesn’t work
2.0.5 + dependencyManagement 2.20 BUILD SUCCESS Default fix
1.21.4 2.12.7 BUILD SUCCESS When annotations can’t change
1.21.3 2.12.7 BadRequestException Doesn’t work

Staying on 1.21.4 also means postponing the 2.0 module renames (the testcontainers- prefix) and package moves. The 2.0.0 release notes list those changes alongside the removal of JUnit 4 support. Issue #11236 is still open as of 2026-10-30. For now, pinning jackson-annotations is the practical way to get onto 2.x.

Summary

  • Testcontainers 2.0.5 shades jackson-databind 2.18.4, but it uses jackson-annotations from your classpath
  • If jackson-annotations 2.14 or older wins, you get NoSuchFieldError: READ_UNKNOWN_ENUM_VALUES_USING_DEFAULT_VALUE. The field was added in 2.15.0
  • Run mvn dependency:tree -Dverbose -Dincludes=com.fasterxml.jackson.core and look for omitted for conflict to find the path that brings in the old version
  • Pin only jackson-annotations to 2.20 in dependencyManagement. Tests pass while jackson-databind stays on 2.12.7
  • If annotations can’t change, 1.21.4 works. 1.21.3 fails on Docker 29 with BadRequestException

References

Leave a Comment