AEM 6.5 LTS is not a feature upgrade — it is a runtime upgrade wearing a familiar version number. Most of the pain in a migration comes from custom code that quietly depended on internals that LTS removes or replaces. Here is what breaks most often in practice.
1. Deprecated and removed APIs
Several APIs that were merely deprecated in 6.5.x are fully removed in LTS. The usual suspects are old Granite UI dialog APIs, legacy Workflow APIs, and some Sling Commons utilities that got replaced by their Jakarta or updated equivalents. A bundle that compiled cleanly against 6.5.23 can fail to resolve entirely against LTS because an imported package no longer exists.
2. Guava version conflicts
LTS ships a different Guava baseline, and if your bundles embed their own Guava version instead of consuming the platform's, you will see NoSuchMethodError or ClassNotFoundException at runtime rather than at build time — which makes this one of the more frustrating failures to trace back to its root cause.
3. Uber JAR repointing
Every project pom that pins the AEM Uber JAR (or the SDK equivalent) needs repointing to the LTS artifact. Skipping this means your build compiles against the wrong API surface and passes locally while failing on the LTS instance.
4. Java version mismatch
LTS requires Java 17 as the baseline runtime. Bundles compiled with older bytecode targets are compatible, but reflection-heavy code, custom class loaders, and any library depending on `sun.*` internal packages tend to break under the stricter module system. This deserves its own migration pass — see the companion article on Java 17 migration for bundles.
Recommended remediation order
- Inventory every custom bundle and its declared package imports/exports
- Repoint the Uber JAR / SDK dependency first, then fix compile errors
- Run a dependency tree check (
mvn dependency:tree) to catch embedded Guava or Jackson conflicts - Deploy to a scratch LTS instance and watch the OSGi console for bundles stuck in Installed state
- Only then move to functional regression testing
Treat this as a dependency and API-surface audit before it becomes a functional testing problem — it is far cheaper to catch a removed API at compile time than to chase a runtime stack trace three environments later.