testcontainers/testcontainers-java · error · ExtensionConfigurationException

FieldName: does not implement Startable

Error message

FieldName: {} does not implement Startable

What it means

TestcontainersExtension (the @Testcontainers/@Container JUnit 5 extension) throws this ExtensionConfigurationException when a field annotated with @Container is not assignable to Startable. Only startable resources (containers or custom Startables) may be managed by the extension.

Solutions

  1. Make the field type implement Startable (e.g. extend GenericContainer<?>)
  2. Remove @Container from fields that are not containers/Startables
  3. Use @Container only on GenericContainer subclasses or Startable implementations
  4. Wrap external resources in a custom Startable implementation if lifecycle management is desired

Example fix

// before
@Container
private MyDbConfig config = new MyDbConfig();
// after
@Container
private PostgreSQLContainer<?> postgres = new PostgreSQLContainer<>("postgres:16");
Defensive patterns

Strategy: type-guard

Validate before calling

for (java.lang.reflect.Field f : testClass.getDeclaredFields()) {
    if (f.isAnnotationPresent(Container.class)
        && !Startable.class.isAssignableFrom(f.getType())) {
        throw new IllegalStateException("@Container field " + f.getName() + " must implement Startable");
    }
}

Type guard

boolean isManagedContainer(java.lang.reflect.Field f) {
    return f.isAnnotationPresent(Container.class)
        && Startable.class.isAssignableFrom(f.getType());
}

Try / catch

try {
    extension.beforeEach(ctx);
} catch (ExtensionConfigurationException e) {
    if (e.getMessage().contains("does not implement Startable")) {
        throw new IllegalStateException("Fix field type or remove @Container", e);
    }
    throw e;
}

Prevention

When it happens

Trigger: Annotating a field with @Container whose type does not implement Startable — e.g. a String, a config object, or a custom wrapper that forgot to extend GenericContainer/implement Startable.

Common situations: Custom lifecycle wrappers not implementing Startable; refactoring changed a field's type; copy-pasting @Container onto an unrelated field.

Understand the failure class

Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.

Related errors


AI-assisted analysis of testcontainers/testcontainers-java@8e549514e3 (2026-09-12). Data as JSON: /api/errors/496f8f319400b466. Report an issue: GitHub.

Appendix: source

Thrown at modules/junit-jupiter/src/main/java/org/testcontainers/junit/jupiter/TestcontainersExtension.java:235

    private Stream<StoreAdapter> findRestartContainers(Object testInstance) {
        return ReflectionSupport
            .findFields(testInstance.getClass(), isRestartContainer(), HierarchyTraversalMode.TOP_DOWN)
            .stream()
            .map(f -> getContainerInstance(testInstance, f));
    }

    private Predicate<Field> isRestartContainer() {
        return isContainer().and(ModifierSupport::isNotStatic);
    }

    private static Predicate<Field> isContainer() {
        return field -> {
            boolean isAnnotatedWithContainer = AnnotationSupport.isAnnotated(field, Container.class);
            if (isAnnotatedWithContainer) {
                boolean isStartable = Startable.class.isAssignableFrom(field.getType());

                if (!isStartable) {
                    throw new ExtensionConfigurationException(
                        String.format("FieldName: %s does not implement Startable", field.getName())
                    );
                }
                return true;
            }
            return false;
        };
    }

    private static StoreAdapter getContainerInstance(final Object testInstance, final Field field) {
        try {
            field.setAccessible(true);
            Startable containerInstance = (Startable) field.get(testInstance);
            if (containerInstance == null) {
                throw new ExtensionConfigurationException("Container " + field.getName() + " needs to be initialized");
            }
            return new StoreAdapter(field.getDeclaringClass(), field.getName(), containerInstance);
        } catch (IllegalAccessException e) {

View on GitHub (pinned to 8e549514e3)