NationalSecurityAgency/ghidra · error · IllegalArgumentException

Overridden constructor must return same or subclass of origi

Error message

Overridden constructor must return same or subclass of original

What it means

Thrown by DependentServiceResolver.addClass() when a @DependentService method specifies an override() target whose class is not assignable from the method's return type. An override replaces a previously-registered factory for a given service class, so the new method must return the same class or a subclass of the target it overrides.

Source

Thrown at Ghidra/Debug/ProposedUtils/src/main/java/generic/depends/DependentServiceResolver.java:82

			addClass(superIf);
		}

		for (Method m : cls.getDeclaredMethods()) {
			DependentService annot = m.getAnnotation(DependentService.class);
			if (annot == null) {
				continue;
			}

			int mods = m.getModifiers();
			if (Modifier.isStatic(mods)) {
				throw new IllegalArgumentException("Constructor must be a non-static method");
			}

			Class<?> override = annot.override();
			Class<?> rCls = m.getReturnType();
			if (override != DependentService.Sentinel.class) {
				if (!override.isAssignableFrom(rCls)) {
					throw new IllegalArgumentException(
						"Overridden constructor must return same or subclass of original");
				}
				depsByDependents.computeIfAbsent(override, o -> new HashSet<>()).add(rCls);
				constructors.put(override, m);
			}
			constructors.put(rCls, m);
			m.setAccessible(true);

			for (Class<?> pType : m.getParameterTypes()) {
				depsByDependents.computeIfAbsent(rCls, c -> new HashSet<>()).add(pType);
			}
		}
		for (Field f : cls.getDeclaredFields()) {
			DependentService annot = f.getAnnotation(DependentService.class);
			if (annot == null) {
				continue;
			}
			Class<?> fCls = f.getType();

View on GitHub (pinned to d5f144c24d)

Solutions

  1. Make the @DependentService method's return type the same as, or a subclass of, the override() target class.
  2. If overriding an interface, ensure the method returns a type that implements that interface.
  3. Correct the override() value to match the actual service hierarchy the method belongs to.

Example fix

// before
@DependentService(override = MyService.class)
public UnrelatedService alt() { ... } // UnrelatedService not a MyService

// after
@DependentService(override = MyService.class)
public MyServiceImpl alt() { ... } // MyServiceImpl extends MyService
Defensive patterns

Strategy: validation

Validate before calling

Class<?> override = annot.override();
Class<?> rCls = m.getReturnType();
if (override != DependentService.Sentinel.class &&
    !override.isAssignableFrom(rCls)) {
  throw new IllegalStateException(
    "override " + override + " not satisfied by return type " + rCls);
}

Prevention

When it happens

Trigger: Writing @DependentService(override=SomeService.class) on a method whose return type is unrelated to SomeService. Overriding a service but returning a sibling type rather than a subclass. Changing the override target without updating the method's return type.

Common situations: Plugin extension points where a plugin overrides a core service with an alternate implementation; refactoring an override's return type to a parallel hierarchy; mismatches between an interface and a concrete override.

Related errors


AI-assisted analysis of NationalSecurityAgency/ghidra@d5f144c24d (2026-08-14). Data as JSON: /api/errors/4e6ae81bd3c8f6b4. Report an issue: GitHub.