jenkinsci/jenkins · error · IllegalStateException

Expected 1 instance of ${type.getName()} but got ${all.size(

Error message

Expected 1 instance of ${type.getName()} but got ${all.size()}

What it means

Thrown by ExtensionList.lookupSingleton when more than one instance of the requested type is registered. lookupSingleton demands exactly one; in unit-test mode an empty list gives a different message, but any count other than 1 (including 0 in non-test mode) hits this branch. This guards callers that cannot disambiguate duplicates.

Source

Thrown at core/src/main/java/hudson/ExtensionList.java:458

    /**
     * Convenience method allowing lookup of the only instance of a given type.
     * Equivalent to {@code ExtensionList.lookup(Class).get(Class)} if there is one instance,
     * and throws an {@code IllegalStateException} otherwise.
     *
     * @param type The type to look up.
     * @return the singleton instance of the given type in its list.
     * @throws IllegalStateException if there are no instances, or more than one
     *
     * @since 2.87
     */
    public static @NonNull <U> U lookupSingleton(Class<U> type) {
        ExtensionList<U> all = lookup(type);
        if (Main.isUnitTest && all.isEmpty()) {
            throw new IllegalStateException("Found no instances of " + type.getName() +
                " registered (possible annotation processor issue); try using `mvn clean test -Dtest=…` rather than an IDE test runner");
        } else if (all.size() != 1) {
            throw new IllegalStateException("Expected 1 instance of " + type.getName() + " but got " + all.size());
        }
        return all.getFirst();
    }

    /**
     * Convenience method allowing lookup of the instance of a given type with the highest ordinal.
     * Equivalent to {@code ExtensionList.lookup(type).get(0)} if there is at least one instance,
     * and throws an {@link IllegalStateException} otherwise if no instance could be found.
     *
     * @param type The type to look up.
     * @return the singleton instance of the given type in its list.
     * @throws IllegalStateException if there are no instances
     *
     * @since 2.435
     */
    public static @NonNull <U> U lookupFirst(Class<U> type) {
        var all = lookup(type);
        if (!all.isEmpty()) {

View on GitHub (pinned to 2e228ff40b)

Solutions

  1. Find and remove the duplicate @Extension registration (check the message's count and inspect manage/extensions).
  2. If duplicates are legitimate, use lookupFirst instead to pick the highest-ordinal instance.
  3. Adjust @Extension ordinal to make one registration win and the other effectively shadowed (still counted, so prefer removal).
  4. In tests, ensure only one instance is registered; reset the ExtensionList between tests.

Example fix

// before
var s = ExtensionList.lookupSingleton(MyService.class); // 2 registered
// after
var s = ExtensionList.lookupFirst(MyService.class); // accepts duplicates, picks top
Defensive patterns

Strategy: validation

Validate before calling

ExtensionList<MyService> all = ExtensionList.lookup(MyService.class);
if (all.size() != 1) {
    // duplicates or none — decide policy before calling lookupSingleton
}

Type guard

static boolean isSingletonRegistered(Class<?> type) {
    return ExtensionList.lookup(type).size() == 1;
}

Try / catch

try {
    var s = ExtensionList.lookupSingleton(MyService.class);
} catch (IllegalStateException e) {
    // 'Expected 1 instance ... but got N' — use lookupFirst or remove the duplicate
    var s = ExtensionList.lookupFirst(MyService.class);
}

Prevention

When it happens

Trigger: Two @Extension classes (or two registrations) of the same type are both loaded; a test registers a mock plus the real one; a plugin and core both register the same extension point; duplicate registrations from a fat jar.

Common situations: Plugin provides an extension point already provided by core or another installed plugin; test harness adds a duplicate; split-package issue loading the same class twice.

Related errors


AI-assisted analysis of jenkinsci/jenkins@2e228ff40b (2026-08-14). Data as JSON: /api/errors/7d4f145eb46de77b. Report an issue: GitHub.