elastic/elasticsearch · critical · IllegalStateException

Expected at most one {} implementation, found: {}

Error message

Expected at most one {} implementation, found: {}

What it means

Thrown by DataStreamsPlugin.loadSingleExtension when more than one plugin registers an implementation of a single-slot extension point (DownsamplingOperations, FrozenTransitionInfoProvider, etc.). These extension points are designed for at most one implementation; multiple registrations are an ambiguous, unsupported deployment state and fail fast at plugin load.

Source

Thrown at modules/data-streams/src/main/java/org/elasticsearch/datastreams/DataStreamsPlugin.java:178

    public DataStreamsPlugin(Settings settings) {
        this.settings = settings;
    }

    @Override
    public void loadExtensions(ExtensionLoader loader) {
        downsamplingOperations = loadSingleExtension(loader, DownsamplingOperations.class, downsamplingOperations);
        frozenTransitionInfoProvider = loadSingleExtension(loader, FrozenTransitionInfoProvider.class, frozenTransitionInfoProvider);
    }

    /**
     * Loads at most one implementation of the given extension point, falling back to {@code fallback} if none is
     * registered. Throws if more than one implementation is found, since these extension points are meant to be
     * implemented by a single optional plugin.
     */
    private static <T> T loadSingleExtension(ExtensionLoader loader, Class<T> type, T fallback) {
        List<T> extensions = loader.loadExtensions(type);
        if (extensions.size() > 1) {
            throw new IllegalStateException(
                "Expected at most one "
                    + type.getSimpleName()
                    + " implementation, found: "
                    + extensions.stream().map(Object::getClass).toList()
            );
        }
        return extensions.isEmpty() ? fallback : extensions.get(0);
    }

    protected Clock getClock() {
        return Clock.systemUTC();
    }

    // The dependency of index.look_ahead_time is a cluster setting and currently there is no clean validation approach for this:
    static void additionalLookAheadTimeValidation(TimeValue lookAhead, TimeValue timeSeriesPollInterval) {
        if (lookAhead.compareTo(timeSeriesPollInterval) < 0) {
            final String message = String.format(
                Locale.ROOT,

View on GitHub (pinned to db6a809a66)

Solutions

  1. Remove one of the conflicting plugins so exactly one (or zero) implementations of the named extension point is installed.
  2. If you need custom behavior, have your plugin replace, not augment, the bundled implementation.
  3. Inspect the loaded extensions list in the error message to identify both conflicting plugin classes.
Defensive patterns

Strategy: validation

Validate before calling

// At install time, enumerate plugins and assert at most one implements each single-slot extension.
Set<Class<?>> singleSlot = Set.of(DownsamplingOperations.class, FrozenTransitionInfoProvider.class);
for (Class<?> t : singleSlot) {
  long count = installedPlugins.stream().filter(p -> Arrays.asList(p.getClass().getInterfaces()).contains(t)).count();
  if (count > 1) throw new IllegalStateException("multiple " + t.getSimpleName() + " implementations installed");
}

Prevention

When it happens

Trigger: Installing two plugins that each provide a DownsamplingOperations or FrozenTransitionInfoProvider; a custom plugin duplicating functionality already provided by a bundled plugin.

Common situations: Custom plugin ships alongside an Elastic plugin that already implements the same extension point; upgrading and accidentally leaving an old custom extension plugin installed.

Related errors


AI-assisted analysis of elastic/elasticsearch@db6a809a66 (2026-08-12). Data as JSON: /api/errors/0446c2792fdc6ce2. Report an issue: GitHub.