# Fixing Common Sling Model Injection Issues

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/bundles` and confirm your `core` bundle is in *Active* state. Then check `/system/console/status-slingmodels` to see whether your model class is listed.
- **Missing package export.** Models must live in a package listed under `Sling-Model-Packages` or `Sling-Model-Classes` in the bundle manifest. The archetype configures this in the `core` module's `pom.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.class` but HTL is adapting from the request (the default for `data-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:**

```java
// 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 `name` attribute in `@ValueMapValue(name = "...")`) must match the JCR property exactly, and it's case-sensitive. A dialog saving `./heroTitle` won't populate a field called `title`.
- **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 `String` being injected into a `Boolean` or `Long` field can fail or return null. Use `{Boolean}true` style 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

- `@ScriptVariable` only works when the model is adapted from a `SlingHttpServletRequest`, since script variables come from the request's scripting context. If your model adapts from `Resource`, use `@SlingObject` for the resolver or `@Self` plus other approaches instead.
- `@Self` is 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/components` for 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 `@Optional` so the model doesn't fail.

## Symptom 6: Multifield or Child Resource Lists Are Empty

- Confirm the multifield dialog's `name` matches the child resource name (`./links` in the dialog means a child node named `links`).
- Use `@ChildResource` for 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:

1. **Is the bundle Active?** (`/system/console/bundles`)
2. **Is the model registered?** (`/system/console/status-slingmodels`)
3. **Check the logs.** Sling Models log the reason for adaptation failures, often naming the field that couldn't be injected. Look at `error.log` for `org.apache.sling.models` messages.
4. **Inspect the actual content** in CRXDE Lite to confirm property names, locations and types.
5. **Check the adaptable** against how HTL (or other code) is invoking the model.
6. **Switch to** `OPTIONAL` temporarily 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\]*
