AEM ENGINEERING NOTES / 002

Why Is My OSGi Component Unsatisfied?

Sling & OSGi · Diagnostic guide

A practical guide to diagnosing unsatisfied OSGi components in Adobe Experience Manager, covering missing services, configuration, target filters, bundle wiring, and upgrade-related issues.

Mohammed Boudoun · Published

In this engineering note

01 — Introduction

An unsatisfied OSGi component is usually not the problem itself — it is a symptom of a missing dependency, invalid configuration, unresolved bundle state, or an incorrect service contract. The runtime is telling you, precisely, that a precondition for activation is not met.

AEM is a graph of bundles, components, services and configurations wired together by the Service Component Runtime. Troubleshooting becomes straightforward once you read that graph instead of guessing: an UNSATISFIED component has a declared requirement the container cannot currently fulfil, and the diagnosis is to find which requirement and why.

This article is a procedure, not OSGi theory. The goal is to take a component stuck in UNSATISFIED and walk it back to the exact missing reference, filter, configuration or bundle wiring that is holding it there.

02 — What does UNSATISFIED actually mean?

Two lifecycles are easy to conflate. A bundle moves through INSTALLED → RESOLVED → ACTIVE, governed by the OSGi framework and package wiring. A component moves through UNSATISFIED → SATISFIED → ACTIVE, governed by the Service Component Runtime (SCR / Declarative Services). They are related but not the same thing.

A bundle can be ACTIVE while one of its components is still UNSATISFIED. The bundle resolved its imports and started, but a component inside it declares a mandatory reference or requires configuration that is not currently available — so SCR refuses to activate it, and it registers no service.

This distinction is the whole game. If the bundle is ACTIVE but the component is UNSATISFIED, stop looking at Import-Package wiring and start looking at the component's references and configuration. If the bundle is only RESOLVED or INSTALLED, the problem is lower down, in bundle wiring itself.

  • Bundle state: INSTALLED → RESOLVED → ACTIVE (framework / package wiring)
  • Component state: UNSATISFIED → SATISFIED → ACTIVE (SCR / DS)
  • Service registration: a component only publishes a service once ACTIVE
  • Service reference: a declared dependency on another service
  • Configuration: a component may require a configuration PID before it can satisfy

03 — First check: which reference is missing?

Open the component in the Components console. For each component it lists the declared references: the service interface, cardinality, policy, any target filter, and whether that reference is currently bound. The unsatisfied reference is named explicitly — read it before doing anything else.

That single line tells you the shape of the problem. If the expected interface has no matching service at all, you are chasing a missing or upstream-unsatisfied provider. If a service of that interface exists but the reference is still unbound, the target filter almost certainly does not match. If the reference is mandatory and static, the component cannot activate until it is bound.

Diagnose the specific reference, not the component in general. 'The component is unsatisfied' is the question; 'the mandatory reference to MetadataProvider with target (provider=aem) is unbound' is the answer you are looking for.

04 — Common causes

Most unsatisfied components reduce to a small set of recurring causes. Work through them against the specific reference the console named.

  • The referenced service is genuinely not registered — often because its own component is unsatisfied upstream
  • The reference points at the wrong interface (implementation class instead of the API)
  • A target filter that matches no registered service
  • Configuration PID mismatch between the component and the config that was deployed
  • A mandatory configuration is missing (configuration policy REQUIRE with no config)
  • Unresolved Import-Package, so the bundle never reaches ACTIVE
  • Classloader / duplicate API versions exporting the same package twice
  • A circular chain of mandatory references that can never all satisfy at once
  • An exception thrown in @Activate, which drops the component back out of ACTIVE
  • Incomplete SCR → Declarative Services annotation migration

05 — AEM-specific failure modes

Several causes show up specifically in AEM, and especially around platform work such as a modular OSGi service architecture or an upgrade. The runtime is standard OSGi, but the context adds its own traps.

  • Custom services referencing AEM APIs that were deprecated or removed in a newer version
  • DS annotation migration: legacy org.apache.felix.scr.annotations not fully moved to org.osgi.service.component.annotations
  • Run-mode-specific configuration present on one instance but missing on another (author vs publish, or a prod run mode)
  • Deployment / package install ordering leaving a dependency temporarily absent
  • A third-party bundle that is not compatible with the target AEM version
  • Java version mismatch between the build toolchain and the AEM runtime

06 — Diagnostic flow

A consistent order turns guesswork into a short walk down the dependency graph. Start at the component and stop at the first step that fails.

Component UNSATISFIED
        |
Check required references      -> which reference is unbound?
        |
Is that service registered?    -> no  -> fix the provider (often unsatisfied upstream)
        |  yes
Check target filter            -> does the filter match a real service?
        |
Check configuration            -> is the required PID present and valid?
        |
Check bundle imports           -> are all Import-Package wired? (bundle ACTIVE?)
        |
Check activation logs          -> did @Activate throw?
        |
Validate dependency chain      -> are transitive references satisfied?

07 — Service reference example

The reference declaration decides the activation rules. Cardinality controls whether a reference is required and whether multiple are allowed; policy controls whether the container rebinds dynamically; the target filter narrows which service instance qualifies.

In the example below the component declares a MANDATORY, STATIC reference with a target filter. If no MetadataProvider is registered, or none carries the property provider=aem, the reference stays unbound and the component stays UNSATISFIED. Mandatory means no activation without it; static means the container will not silently rebind a late arrival into a running component.

@Component(service = ContentEnrichmentService.class, immediate = true)
public class ContentEnrichmentServiceImpl implements ContentEnrichmentService {

    @Reference(
        cardinality = ReferenceCardinality.MANDATORY,
        policy = ReferencePolicy.STATIC,
        target = "(provider=aem)"
    )
    private MetadataProvider metadataProvider;

    @Activate
    protected void activate() {
        // Runs only once every mandatory reference is bound.
    }
}

08 — Configuration example

Configuration can keep a component inactive even when every service reference is available. A component whose configuration policy is REQUIRE will not satisfy until a configuration for its PID exists; a PID mismatch between the component and the deployed configuration produces exactly the same symptom.

Define the configuration shape with an object class definition, and deliver the values as a PID-named configuration — optionally scoped to a run mode. If the required attributes are absent or the file name does not match the component PID, the component remains UNSATISFIED rather than activating with partial settings.

@Component(configurationPolicy = ConfigurationPolicy.REQUIRE)
@Designate(ocd = ContentEnrichmentServiceImpl.Config.class)
public class ContentEnrichmentServiceImpl implements ContentEnrichmentService {

    @ObjectClassDefinition(name = "Content Enrichment Service")
    public @interface Config {
        @AttributeDefinition(name = "API endpoint")
        String endpoint();          // required
        @AttributeDefinition(name = "Timeout (ms)")
        int timeout() default 5000; // optional
    }
}

// Deployed, run-mode scoped configuration (OSGi config, JSON form):
// com.example.core.ContentEnrichmentServiceImpl~prod.cfg.json
// {
//   "endpoint": "https://api.internal/enrich",
//   "timeout": 8000
// }

09 — Logs & console checks

The web console and the logs answer different questions, and you usually need both. The exact UI layout varies between AEM versions, but the underlying consoles and concepts are stable.

Read the component first, then widen out: the Components console names the unsatisfied reference; the Bundles console reveals a bundle stuck below ACTIVE or an unresolved Import-Package; the Services console confirms whether the expected interface is actually registered and with which properties. When a component flickers in and out of ACTIVE, the error log holds the activation stack trace that explains why.

  • Components: /system/console/components — component state and the unsatisfied reference
  • Bundles: /system/console/bundles — RESOLVED-not-ACTIVE and unresolved imports
  • Services: /system/console/services — is the interface registered, with what properties
  • error.log — activation exceptions and stack traces from @Activate
  • Confirm the service properties actually match the reference target filter

10 — Why this appears after an AEM upgrade

Components that worked for years can turn UNSATISFIED immediately after an upgrade, because the surface they depend on moved. This is a dependency-graph change, not a regression in your logic, and it is a routine part of an AEM 6.5 LTS modernization or a version migration.

The usual culprits are services that were removed or relocated, deprecated APIs that no longer resolve, bundle wiring that changed so an Import-Package now points nowhere, a Java version change in the runtime, dependency versions that shifted underneath you, and incomplete SCR → OSGi Declarative Services annotation migration. Each of these breaks a specific edge in the graph; find the edge and the fix is local.

11 — Anti-patterns

Most time lost on unsatisfied components is lost to reactions that hide the cause instead of finding it.

  • Retrying deployments or restarting instances without first identifying the missing reference
  • Making every reference OPTIONAL so the component activates with null collaborators
  • Broad or absent target filters that bind the wrong service and move the failure downstream
  • Relying on activation order instead of declaring real dependencies
  • Swallowing activation exceptions, so a failing @Activate looks like a mysterious unsatisfied state

12 — Debugging checklist

A compact pass that resolves most cases, in order:

  • Is the bundle ACTIVE, or only RESOLVED / INSTALLED?
  • Which specific reference does the Components console report as unsatisfied?
  • Is a service of that interface registered at all?
  • Does the target filter match the registered service's properties?
  • Is the required configuration PID present, correctly named, and valid?
  • Are all Import-Package statements resolved?
  • Did @Activate throw — is there a stack trace in error.log?
  • Are the transitive references of that service themselves satisfied?

13 — My perspective

Treat OSGi activation as a dependency graph problem, not a restart problem. An unsatisfied component is a precise statement about a missing edge in that graph, and the console will name it if you ask. Restart-and-hope discards that information and resets you to zero.

In practice — and it is the same discipline behind durable production troubleshooting — I read the component, name the unsatisfied reference, confirm whether the provider exists, then check filter, configuration, imports and activation logs in that order. The cause is almost always one specific, nameable thing, and naming it is most of the fix.

Design to make this easy for the next engineer: declare real dependencies, keep target filters specific, require the configuration you actually need, and never swallow activation exceptions. A component that fails loudly and precisely is a component that is quick to repair.

14 — Conclusion

Unsatisfied is a diagnosis, not a dead end. Separate bundle state from component state, read the specific reference the runtime is waiting on, and walk the dependency chain from there through filters, configuration, bundle wiring and activation logs.

Done systematically, the state that looks opaque becomes a short, repeatable investigation — and the same method holds whether the trigger is a new service, a missing configuration or an AEM upgrade that moved the ground beneath a component.

References

Related engineering work