Fixing Common Sling Model Injection Issues
Series: AEM Core Development — Part 5

Few things frustrate new AEM developers more than a component that renders blank, or a page that throws an error because a Sling Model failed to instantiate. The good news: most injection problems fall into a handful of patterns. This post walks through the usual suspects and how to diagnose each.
Symptom 1: The Model Is null in HTL
If ${hero.title} renders nothing and the whole model seems missing, Sling failed to adapt the request or resource to your class.
Common causes:
- The bundle isn't active or the model isn't registered. Check
/system/console/bundlesand confirm yourcorebundle is in Active state. Then check/system/console/status-slingmodelsto see whether your model class is listed. - Missing package export. Models must live in a package listed under
Sling-Model-PackagesorSling-Model-Classesin the bundle manifest. The archetype configures this in thecoremodule'spom.xml(via the bnd plugin), so if you add a new package, make sure it's covered. - Wrong adaptable. If your model declares
adaptables = Resource.classbut HTL is adapting from the request (the default fordata-sly-use), or the reverse, adaptation fails. Match the adaptable to how the model is used. - Typo in the class name inside
data-sly-use. HTL fails silently.
Symptom 2: Required Field Missing, Whole Model Fails
By default, injected fields are required. If one property doesn't exist on the resource, the entire model fails to instantiate, and you get null in HTL.
Fix options:
// Option A: make the whole model lenient
@Model(adaptables = Resource.class,
defaultInjectionStrategy = DefaultInjectionStrategy.OPTIONAL)
// Option B: make a single field optional
@ValueMapValue
@Optional
private String subtitle;
// Option C: provide a default
@ValueMapValue
@Default(values = "Welcome")
private String title;
Because authors routinely leave fields empty or add new fields to dialogs later (leaving older content without the property), OPTIONAL is a safe default for most content models.
Symptom 3: Field Is Always null Even Though the Property Exists
The model instantiates, but a specific field is empty.
Check these:
- Name mismatch. The field name (or the
nameattribute in@ValueMapValue(name = "...")) must match the JCR property exactly, and it's case-sensitive. A dialog saving./heroTitlewon't populate a field calledtitle. - Wrong location. The property may live on a child node or on a different resource than the one the model is adapted from. Inspect the content in CRXDE Lite (
/content/.../jcr:content/...) to see where the value actually is. - Wrong type. A property saved as a
Stringbeing injected into aBooleanorLongfield can fail or return null. Use{Boolean}truestyle types in dialogs where needed, and check how the value is stored. - Dialog was changed after content was authored. Existing content won't have new properties until authors re-save the component.
Symptom 4: @ScriptVariable or @SlingObject Doesn't Inject
@ScriptVariableonly works when the model is adapted from aSlingHttpServletRequest, since script variables come from the request's scripting context. If your model adapts fromResource, use@SlingObjectfor the resolver or@Selfplus other approaches instead.@Selfis useful to inject the adaptable itself, or to compose models.
Symptom 5: @OSGiService Is null
- The service bundle may not be active, or the service may not be registered (check
/system/console/componentsfor the service component's state; it may be unsatisfied because of a missing configuration or dependency). - The interface used in the field must match what the service registers.
- If the service is optional, mark the field with
@Optionalso the model doesn't fail.
Symptom 6: Multifield or Child Resource Lists Are Empty
- Confirm the multifield dialog's
namematches the child resource name (./linksin the dialog means a child node namedlinks). - Use
@ChildResourcefor composite multifields, and make sure the generic type matches (List<Resource>or a child model class). - If using a child model class, it must also be a valid Sling Model adaptable from
Resource.
A Debugging Checklist
When a model misbehaves, work through this in order:
- Is the bundle Active? (
/system/console/bundles) - Is the model registered? (
/system/console/status-slingmodels) - Check the logs. Sling Models log the reason for adaptation failures, often naming the field that couldn't be injected. Look at
error.logfororg.apache.sling.modelsmessages. - Inspect the actual content in CRXDE Lite to confirm property names, locations and types.
- Check the adaptable against how HTL (or other code) is invoking the model.
- Switch to
OPTIONALtemporarily to isolate which field is causing failure.
Prevent Problems Early
- Use injector-specific annotations (
@ValueMapValue,@ChildResource,@OSGiService) instead of generic@Inject - Write unit tests with AEM Mocks, so injection issues surface at build time rather than on the page
- Handle nulls in getters and
@PostConstruct, since authors will always find a way to leave something blank
What's Next
So far we've worked with content stored directly on pages. The final post in this series moves to Content Fragment Models, the foundation for headless and structured content in AEM.
Next in this series: [Content Fragment Models for Headless]
About the author
- AEM Technical Lead experienced with building and architecting solutions on AEM Sites and Assets. Sharing practical tutorials, real-world implementation lessons, and best practices to help fellow developers and leads navigate AEM.
Comments
Join the discussionWhat's the strangest Sling Model injection bug you've debugged? Share the symptom and the fix below. Real cases help everyone who searches for the same problem later.
AEM Core Development
"Hands-on component building, Sling Models, HTL, and dialog configuration — the everyday toolkit every AEM developer needs."






