spring-projects/spring-framework · error · UnsatisfiedDependencyException
Unsatisfied dependency expressed through field
Error message
Unsatisfied dependency expressed through field
What it means
Thrown as an UnsatisfiedDependencyException when the BeanFactory cannot resolve a value for an @Autowired/@Value field at runtime in an AOT-processed application. The AOT-generated AutowiredFieldValueResolver calls resolveDependency for the field; if that lookup fails (missing bean, ambiguous type, conversion error) the underlying BeansException is wrapped with the InjectionPoint so you can see which field is broken. It is the AOT equivalent of the reflection-based AutowiredAnnotationBeanPostProcessor failure.
Source
Thrown at spring-beans/src/main/java/org/springframework/beans/factory/aot/AutowiredFieldValueResolver.java:188
String beanName = registeredBean.getBeanName();
Class<?> beanClass = registeredBean.getBeanClass();
ConfigurableBeanFactory beanFactory = registeredBean.getBeanFactory();
DependencyDescriptor descriptor = new DependencyDescriptor(field, this.required);
descriptor.setContainingClass(beanClass);
if (this.shortcutBeanName != null) {
descriptor = new ShortcutDependencyDescriptor(descriptor, this.shortcutBeanName);
}
Set<String> autowiredBeanNames = new LinkedHashSet<>(1);
TypeConverter typeConverter = beanFactory.getTypeConverter();
try {
Assert.isInstanceOf(AutowireCapableBeanFactory.class, beanFactory);
Object value = ((AutowireCapableBeanFactory) beanFactory).resolveDependency(
descriptor, beanName, autowiredBeanNames, typeConverter);
registerDependentBeans(beanFactory, beanName, autowiredBeanNames);
return value;
}
catch (BeansException ex) {
throw new UnsatisfiedDependencyException(null, beanName, new InjectionPoint(field), ex);
}
}
private Field getField(RegisteredBean registeredBean) {
Field field = ReflectionUtils.findField(registeredBean.getBeanClass(), this.fieldName);
Assert.notNull(field, () -> "No field '" + this.fieldName + "' found on " +
registeredBean.getBeanClass().getName());
return field;
}
}
View on GitHub (pinned to 69bf83ad71)
Solutions
- Read the InjectionPoint in the stack trace to identify the exact field and its declaring bean, then verify a bean of that type (and qualifier) is registered.
- If the dependency is optional, build the resolver with forField(name) instead of forRequiredField(name), or annotate the field with @Autowired(required=false).
- Add an explicit bean name via .withShortcut(beanName) or a @Qualifier on the field when multiple candidates of the same type exist.
- Ensure placeholder/value resolution is configured (e.g. PropertySourcesPlaceholderConfigurer) and that referenced properties exist in the environment.
- Re-run with the JVM (non-AOT) build to reproduce the same UnsatisfiedDependencyException for a friendlier, full-context stack trace.
Example fix
// before: required field with no qualifying bean
@AutowiredFieldValueResolver.forRequiredField("dataSource")
.resolveAndSet(registeredBean, instance);
// after: optional injection that tolerates absence
AutowiredFieldValueResolver.forField("dataSource")
.resolveAndSet(registeredBean, instance); Defensive patterns
Strategy: try-catch
Validate before calling
// Before resolving, confirm the candidate exists and is unique
ConfigurableListableBeanFactory bf = registeredBean.getBeanFactory();
String[] names = bf.getBeanNamesForType(field.getType());
if (names.length == 0 && required) {
throw new IllegalStateException("No bean of type " + field.getType() + " for field " + field);
} Try / catch
try {
resolver.resolveAndSet(registeredBean, instance);
} catch (UnsatisfiedDependencyException ex) {
InjectionPoint ip = ex.getInjectionPoint();
// log ip, fall back to a default, or rethrow as a domain-specific error
throw new ConfigurationException("Cannot wire field " + (ip != null ? ip.getField() : "?"), ex);
} Prevention
- Run an ApplicationContext integration test that asserts all @Autowired fields resolve in the AOT context.
- Prefer constructor injection over field injection so missing dependencies fail fast at startup.
- Use @Qualifier on field injection points whenever multiple candidates of the type exist.
- Keep the native-image subset (Spring AOT) in sync with bean registrations when trimming the context.
When it happens
Trigger: Calling resolveAndSet(registeredBean, instance) or resolveValue on an AutowiredFieldValueResolver built forField/forRequiredField when beanFactory.resolveDependency(descriptor, ...) throws a BeansException. This happens when the declared field type has no candidate bean, multiple unqualified candidates, or a value that cannot be converted to the field type.
Common situations: Forgetting to register a bean of the field's type in the AOT-optimized context; using @Value('${missing.prop}') without a PropertySourcesPlaceholderConfigurer; a qualifier that no bean satisfies; circular/missing bean after trimming the context for native image; renaming a bean without updating the @Qualifier on the field.
Related errors
- Unsatisfied dependency expressed through parameter
- Unsatisfied dependency expressed through injection point
- No unique bean definition
- Invalid autowire-marked constructors: {}. Found constructor
- Injection of autowired dependencies failed
AI-assisted analysis of spring-projects/spring-framework@69bf83ad71 (2026-08-09).
Data as JSON: /api/errors/4acfa4d1350c62a9.
Report an issue: GitHub.