NationalSecurityAgency/ghidra · error · ServiceConstructionException

No service constructor for {}

Error message

No service constructor for {}

What it means

Thrown by DependentServiceResolver.injectServices() as a ServiceConstructionException when, after running all ordered constructors, some @DependentService field types still have no constructed instance to inject. This is a defensive post-condition: compile() should normally prevent it, so hitting it signals an inconsistency between the compiled order and the field set (e.g. a field type that has a producer key but whose service instance was never populated during the run).

Source

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

			if (service == null) {
				service = cons.construct(obj, instancesByClass);
				constructed.put(cons.method, service);
			}
			instancesByClass.put(cons.cls, service);
			Set<Field> fields = fieldsByClass.remove(cons.cls);
			if (fields != null) {
				for (Field f : fields) {
					try {
						f.set(obj, service);
					}
					catch (IllegalArgumentException | IllegalAccessException e) {
						throw new AssertionError(e);
					}
				}
			}
		}
		if (!fieldsByClass.isEmpty()) {
			throw new ServiceConstructionException(
				"No service constructor for " + fieldsByClass.keySet(), null);
		}
	}
}

View on GitHub (pinned to d5f144c24d)

Solutions

  1. Verify each @DependentService field's declared type exactly matches (or is the override key of) a registered producer's return type.
  2. Review override() mappings to ensure they do not hide the producer the field needs.
  3. Simplify the DI graph and re-add fields incrementally to isolate the offending field.
  4. Report with the fieldsByClass key set from the message to identify the unpopulated field types.
Defensive patterns

Strategy: try-catch

Try / catch

try {
  DependentServiceResolver.inject(obj);
} catch (ServiceConstructionException e) {
  if (e.getMessage().startsWith("No service constructor for")) {
    Msg.error(this, "Injection incomplete; unpopulated fields: " + e.getMessage());
  } else throw e;
}

Prevention

When it happens

Trigger: A field type whose producer was registered under a different class key than the field's declared type. An override mapping that shadows the real producer. A scenario where two field types share a producer method but only one gets keyed into instancesByClass. Generally an internal/edge inconsistency rather than a simple missing producer (that is error 568).

Common situations: Complex override configurations; field types that are supertypes of the produced class but not the exact key used; partial refactors of the DI registration that leave the field map and constructor map out of sync.

Related errors


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