jenkinsci/jenkins · error · LinkageError

Failed to resolve ${c}

Error message

Failed to resolve ${c}

What it means

Thrown by GuiceExtensionResolver.resolve(Class) when introspecting an extension class's @Inject fields, methods, and constructors raises a RuntimeException. Jenkins deliberately converts this to a LinkageError because a class that cannot have its injectable members resolved is effectively missing a dependency. The original RuntimeException is the cause.

Source

Thrown at core/src/main/java/hudson/ExtensionFinder.java:557

                                method.getAnnotatedParameterTypes();
                                method.getParameterAnnotations();
                            }
                        }

                        // See com.google.inject.spi.InjectionPoint(TypeLiteral, Field, boolean)
                        for (Field f : cc.getDeclaredFields()) {
                            if (f.isAnnotationPresent(javax.inject.Inject.class)
                                    || f.isAnnotationPresent(jakarta.inject.Inject.class)
                                    || f.isAnnotationPresent(com.google.inject.Inject.class)) {
                                f.getAnnotations();
                                f.getAnnotatedType().getAnnotations();
                                resolve(f.getType(), encountered);
                            }
                        }
                    }
                    LOGGER.log(Level.FINER, "{0} looks OK", c);
                } catch (RuntimeException x) {
                    throw new LinkageError("Failed to resolve " + c, x);
                }
            }

            @SuppressWarnings({"unchecked", "ChainOfInstanceofChecks"})
            @Override
            protected void configure() {

                bindListener(Matchers.any(), this);

                for (final IndexItem<?, Object> item : index) {
                    boolean optional = isOptional(item.annotation());
                    try {
                        AnnotatedElement e = item.element();
                        Annotation a = item.annotation();
                        if (!isActive(a, e))   continue;

                        Scope scope = optional ? QUIET_FAULT_TOLERANT_SCOPE : FAULT_TOLERANT_SCOPE;
                        if (e instanceof Class) {

View on GitHub (pinned to 2e228ff40b)

Solutions

  1. Inspect the cause: NoClassDefFoundError/TypeNotPresentException names the missing type.
  2. Install or upgrade the plugin/library that provides the missing class.
  3. If the field is truly optional, remove @Inject or mark the extension optional in the index (@Optional).
  4. Rebuild the plugin to align compile-time and runtime dependencies.

Example fix

null
Defensive patterns

Strategy: try-catch

Validate before calling

null

Type guard

null

Try / catch

try {
    // trigger Guice extension resolution
} catch (LinkageError e) {
    // 'Failed to resolve' — read cause for the missing type
    if (e.getCause() instanceof NoClassDefFoundError) { /* install missing dependency */ }
    else throw e;
}

Prevention

When it happens

Trigger: An @Inject field/method/constructor references a type whose class is missing or fails to load; a reflection call (getDeclaredFields/getDeclaredMethods) throws for a malformed class; resolve(f.getType()) triggers a NoClassDefFoundError or TypeNotPresentException during Guice binding setup.

Common situations: Plugin missing a transitive dependency jar; class compiled against an API version not present at runtime; optional dependency that is actually required by an @Inject field; class file corruption causing reflection to throw.

Related errors


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