NationalSecurityAgency/ghidra · error · UnsatisfiedFieldsException

There are fields without suitable constructors: {}

Error message

There are fields without suitable constructors: {}

What it means

Thrown by DependentServiceResolver.compile() as an UnsatisfiedFieldsException when one or more @DependentService-annotated fields have a type for which no @DependentService factory method (constructor) was registered. Every injected field must have a matching producer, otherwise the resolver cannot populate it. The exception carries the set of unsatisfied field types.

Source

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

				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();
			fieldsByClass.computeIfAbsent(fCls, c -> new HashSet<>()).add(f);
			f.setAccessible(true);
		}
	}

	private void compile() throws UnsatisfiedParameterException, UnsatisfiedFieldsException {
		Set<Class<?>> missing = new HashSet<>(fieldsByClass.keySet());
		missing.removeAll(constructors.keySet());
		if (!missing.isEmpty()) {
			throw new UnsatisfiedFieldsException(missing);
		}
		Set<Class<?>> unordered = new HashSet<>(constructors.keySet());
		while (!unordered.isEmpty()) {
			Set<Class<?>> forRound = new HashSet<>(unordered);
			forRound.removeAll(depsByDependents.keySet());
			if (forRound.isEmpty()) {
				throw new UnsatisfiedParameterException(unordered);
			}
			for (Class<?> ready : forRound) {
				Method m = constructors.get(ready);
				unordered.remove(ready);
				ordered.add(new DependentServiceConstructor<>(ready, m));
				for (Iterator<Set<Class<?>>> iterator =
					depsByDependents.values().iterator(); iterator.hasNext();) {
					Set<Class<?>> deps = iterator.next();
					deps.remove(ready);
					if (deps.isEmpty()) {
						iterator.remove();

View on GitHub (pinned to d5f144c24d)

Solutions

  1. Add a @DependentService method whose return type matches each unsatisfied field type reported in the exception.
  2. Remove the @DependentService annotation from fields you no longer want injected.
  3. If the field type is an interface, ensure some method returns a concrete implementation assignable to it.
  4. Inspect the exception's missing set to see exactly which types lack producers.

Example fix

// before
public class Owner {
  @DependentService private Thing thing; // no method returns Thing
}

// after
public class Owner {
  @DependentService private Thing thing;

  @DependentService
  public Thing buildThing() { return new ThingImpl(); } // producer added
}
Defensive patterns

Strategy: validation

Validate before calling

Set<Class<?>> fieldTypes = collectDependentFieldTypes(cls);
Set<Class<?>> producedTypes = collectDependentReturnTypes(cls);
if (!fieldTypes.containsAll(producedTypes == null ? fieldTypes : fieldTypes)) {
  // simpler: ensure every field type has a producer
}
fieldTypes.removeAll(producedTypes);
if (!fieldTypes.isEmpty()) {
  throw new IllegalStateException("No producer for fields: " + fieldTypes);
}

Try / catch

try {
  DependentServiceResolver.get(cls);
} catch (UnsatisfiedFieldsException e) {
  Msg.error(this, "Unsatisfied @DependentService fields: " + e.getMissing());
}

Prevention

When it happens

Trigger: Annotating a field with @DependentService but never writing a @DependentService method that returns that field's type. Removing a factory method while leaving its consuming field in place. Field type is an interface whose only producer was deleted.

Common situations: Adding a new dependency field and forgetting the producer; refactoring that renames/moves a service class so the field type no longer matches any return type; incomplete feature branches.

Related errors


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