Redis extension fails to resolve AWS Secrets Manager dependency on Lucee

Redis extension on Lucee 7 fails to resolve due to a Bundle-SymbolicName mismatch in the AWS Secrets Manager dependency. We manually patched the jar, cleared the OSGi cache, and restarted, but the error persists. Direct classloading bypasses OSGi and works, but causes side effects on unrelated CFCs. Looking for guidance on whether we’re missing a step or this is a known issue.


First of all: yes, I know this is long. :sweat_smile:

But this report is the result of roughly three fairly intense days of debugging, during which we went from “Redis doesn’t work” to inspecting OSGi manifests, patching jars by hand, clearing Felix caches, bypassing OSGi entirely and eventually breaking something completely unrelated.

So I thought it was worth documenting the whole journey, including the dead ends.

One thing feels very strange to me: I assume redis.extension is fairly widely used, so I would expect a problem this fundamental to have surfaced much more often.

That makes me suspect I may still be missing something obvious somewhere - but after three days of testing different versions, JVMs, manifests, bundle names, caches and workarounds, I genuinely can’t find a way out of this.

So if there is something stupidly simple I’m overlooking, I will be very happy to be embarrassed by the answer. :sweat_smile:

Environment

  • Lucee: 7.0.5-SNAPSHOT+41

  • CommandBox: 6.3.4+00883

  • Java: 11.0.29 (Ubuntu build, Eclipse Adoptium-equivalent)

  • Also reproduced identically on OpenJDK 21, Lucee 7’s recommended target, so the JVM version does not appear to be a factor.

  • OS: Ubuntu, CommandBox-managed Runwar/Undertow

  • redis.extension versions tested:

    • 3.0.1.0 — javax, expected to fail on Jakarta
    • 4.0.0.2
    • 4.1.0.0-SNAPSHOT

All three eventually fail on the same missing OSGi dependency.

The error

org.osgi.framework.BundleException: Unable to resolve redis.extension [N](R N.0): missing
requirement [redis.extension [N](R N.0)] osgi.wiring.bundle;
(&(osgi.wiring.bundle=org.lucee.aws-java-sdk-secretsmanager-all)(bundle-version>=1.12.758)(!(bundle-version>=2.0.0)))
Unresolved requirements: [[redis.extension [N](R N.0)] osgi.wiring.bundle;
(&(osgi.wiring.bundle=org.lucee.aws-java-sdk-secretsmanager-all)(bundle-version>=1.12.758)(!(bundle-version>=2.0.0)))]

On OpenJDK 21, Lucee also logs this more explicit message:

Lucee was not able to download/load the following bundles [org.lucee.aws-java-sdk-secretsmanager-all:1.12.758;]

The failure appears the first time the application actually touches the named cache — for example with cachePut(), cacheGet() or cacheClear() — using a cache connection backed by redis.extension.

Example:

// Application.cfc
this.cache.connections["RedisCache"] = {
    class: "lucee.extension.io.cache.redis.simple.RedisCache",
    bundleName: "redis.extension",
    bundleVersion: "4.1.0.0-SNAPSHOT",
    storage: false,
    custom: [ /* host/port/password/... */ ],
    default: "object"
};

this.cache.object = "RedisCache";

First clue: Lucee downloads a dependency under a different symbolic name

Lucee correctly identifies the missing dependency and tries to fetch it:

"bundle-download","Could not find [/bundles/org.lucee.aws-java-sdk-secretsmanager-all-1.12.758.jar] in lucee.jar"

"bundle-download","Downloading bundle [org.lucee.aws-java-sdk-secretsmanager-all:1.12.758] from
https://repo1.maven.org/maven2/org/lucee/awssecretsmanager/1.12.758/awssecretsmanager-1.12.758.jar
and copying to .../WEB-INF/lucee-server/bundles/org.lucee.aws-java-sdk-secretsmanager-all-1.12.758.jar"

The download itself succeeds: a valid jar of roughly 341 KB appears in WEB-INF/lucee-server/bundles/.

But the bundle actually ends up as:

org.lucee.aws-secretsmanager-1.12.758.jar

while Redis requires:

org.lucee.aws-java-sdk-secretsmanager-all-1.12.758.jar

We manually renamed the jar and restarted Lucee.

Lucee simply downloaded it again and recreated the shorter-named bundle on the next resolution attempt.

At this stage, this strongly suggested that the issue was not the filesystem filename itself, but the bundle identity declared inside the jar.

That suspicion was later confirmed directly by inspecting the manifest.

Confirmed: the published jar has a different Bundle-SymbolicName

We extracted the downloaded jar and inspected:

META-INF/MANIFEST.MF

The relevant values are:

Bundle-Name: org.lucee.aws-secretsmanager
Bundle-SymbolicName: org.lucee.aws-secretsmanager
Bundle-Version: 1.12.758

But redis.extension requires:

org.lucee.aws-java-sdk-secretsmanager-all

So the version is correct, but the symbolic name is not the one required by Redis.

We also checked the other bundles referenced by this jar:

org.lucee.aws-java-sdk-core
org.lucee.aws-jmespath-java

Both are already present and expose the expected Bundle-SymbolicName.

The Secrets Manager bundle is the only mismatch we found.

We patched the manifest by hand

Because apparently debugging Redis had not become strange enough yet, we patched the jar ourselves.

We changed:

Bundle-Name: org.lucee.aws-secretsmanager
Bundle-SymbolicName: org.lucee.aws-secretsmanager

to:

Bundle-Name: org.lucee.aws-java-sdk-secretsmanager-all
Bundle-SymbolicName: org.lucee.aws-java-sdk-secretsmanager-all

A small side adventure: the manifest uses CRLF line endings, so our first sed command with a $ end-of-line anchor did not match. cat -A eventually revealed the friendly little ^M.

We then:

  1. Repackaged the jar with jar cfm.

  2. Saved it as:

    org.lucee.aws-java-sdk-secretsmanager-all-1.12.758.jar
    
  3. Verified the actual resulting jar again using:

    unzip -p ... | grep Bundle-
    
  4. Confirmed that the symbolic name and version were now exactly what Redis requires.

  5. Cleared Lucee’s separate OSGi felix-cache.

  6. Performed a full server stop/start.

The OSGi bundle ID assigned to redis.extension changed from [73] to [52], confirming that this was a genuinely fresh registration rather than an old cached resolution attempt.

And then…

exactly the same error.

BundleException: Unable to resolve redis.extension ...
missing requirement org.lucee.aws-java-sdk-secretsmanager-all

So at this point we had:

  • the exact expected filename;
  • the exact expected Bundle-SymbolicName;
  • the correct Bundle-Version;
  • a freshly rebuilt jar;
  • a cleared Felix cache;
  • a full server restart;

…and Lucee still refused to resolve it.

That was the point where we stopped blaming the filename. :sweat_smile:

Possible second issue: bundles may not simply be discovered from the bundles directory

This result suggests that:

WEB-INF/lucee-server/bundles/

is probably not simply scanned for arbitrary valid OSGi bundles at startup.

There may be some additional internal registry/index/install state that determines which bundles Lucee considers installed and loadable.

This would also fit another thing we observed: the S3 Resource Extension appeared in the Lucee Administrator as “not loaded” despite its files being present.

If such a registry exists, simply dropping a perfectly valid jar into bundles/ may not be sufficient.

We could not take this part much further without knowing more about Lucee’s internal extension/bundle installation machinery.

Side note: 4.1.0.0 exists as a Git tag but is not installable

The lucee/extension-redis repository has a 4.1.0.0 Git tag dated 2026-05-24.

Naturally, we assumed that dropping -SNAPSHOT and using:

bundleVersion: "4.1.0.0"

might be worth trying.

It fails immediately:

org.osgi.framework.BundleException: The OSGi Bundle with name [redis.extension] in version [4.1.0.0]
is not available locally [...bundles] or from the update provider [https://update.lucee.org].

So as far as we can tell, 4.1.0.0 is a source tag but was never published as an installable binary through Lucee’s extension provider.

4.1.0.0-SNAPSHOT is currently the installable 4.1 version.

This seems worth mentioning because it looks like the 4.x publish/release path may have more than one gap.

Side note: org.lucee:redis-extension does not appear to exist on Maven Central

Lucee’s documentation uses this as an example for LUCEE_EXTENSIONS / -Dlucee.extensions:

org.lucee:redis-extension:3.0.0.56

We checked Maven Central directly for:

g:org.lucee AND a:redis-extension

and found no matching artifact.

The only Redis-related artifact under org.lucee we found there was launchdarkly-redis, which is unrelated.

So Redis appears to be distributed through Lucee’s own extension infrastructure rather than Maven Central, despite the Maven-style syntax used in that documentation example.

Confirmed: 1.12.758 is already the newest available Secrets Manager bundle

We also checked whether a later AWS Secrets Manager wrapper might satisfy Redis’s required range:

>= 1.12.758
< 2.0.0

Available org.lucee:awssecretsmanager versions include:

1.12.758
1.12.455
1.12.454
1.11.877.0002L
1.11.877.0001L
1.11.877.0000L
1.11.877

So 1.12.758 is already the newest available version.

There is no later artifact in the required range to try instead.

The S3-extension workaround did not work for us

We found an older Lucee forum thread where someone reported that installing the AWS S3 extension first allowed the Redis extension to resolve afterward.

The theory is plausible: the S3 extension may install another correctly wrapped copy of the AWS SDK dependency Redis needs.

We tried it.

  • Installed S3 Resource Extension 2.0.3.1 through Lucee Server Administrator.
  • It appeared in the bundle list as “not loaded”.
  • Lucee offered an update to 2.1.0.4-beta.
  • Attempting the update failed silently in the Admin UI.
  • The installed version remained 2.0.3.1.

So unfortunately we could not reproduce the workaround reliably in this Lucee 7 environment.

There are also separate reports of S3 extension installation problems on Lucee 7, so this may simply be another manifestation of the same broader extension-resolution problem.

The nuclear option: bypass OSGi completely

After enough time spent trying to make OSGi happy, we asked a slightly different question:

Does the Redis extension’s actual Java code work?

Surprisingly — or perhaps unsurprisingly by this point — yes.

The implementation class is:

lucee.extension.io.cache.redis.simple.RedisCache

and the runtime functionality we use only appears to need org.mongodb.bson for object serialization/deserialization.

So we loaded the jars directly:


this.javaSettings = {
    LoadPaths         = [ "/libs/redis-extension" ],
    loadCFMLClassPath = true,
    reloadOnChange    = false
};

redisCache = createObject(
    "java",
    "lucee.extension.io.cache.redis.simple.RedisCache"
).init();

redisCache.init({
    host: "...",
    port: "...",
    password: "..."
});

redisCache.put(
    "key",
    "value",
    javaCast("long", 30000),
    javaCast("long", 30000)
);

redisCache.getValue("key");

And this works.

We tested:

  • init
  • put
  • getValue
  • contains
  • remove

against a real Redis instance.

All worked correctly.

No AWS SDK involved.

No OSGi.

No Server Administrator.

For approximately twenty glorious minutes, we thought we had won.

Unfortunately, direct classloading appears unsafe in our application

Once we wired that implementation into our application’s cache manager — which is used on essentially every request — we started seeing an apparently unrelated error elsewhere:

java.lang.NullPointerException
Cannot invoke "lucee.runtime.component.ComponentPageRef.get(lucee.runtime.PageContext)"
because "this.cpRef" is null

This came from GetMetadata() on perfectly ordinary CFC bean instances inside an unrelated bean-to-JSON utility.

The important observations were:

  • the error appeared repeatedly after introducing the direct Java classloading workaround;

  • it affected unrelated CFC code;

  • a full server restart did not fix it while the workaround remained active;

  • replacing the custom Redis implementation with Lucee’s built-in

    lucee.runtime.cache.ram.RamCache
    

    made the problem disappear completely.

The direct Redis object was created once and cached for the application’s lifetime, so this does not appear to be caused by repeatedly creating classloaders.

We did not investigate this enough to call it a Lucee bug with confidence.

Our current suspicion is that:

createObject("java", className, jarPaths)

with explicit jars may share more classloading machinery with Lucee than we expected, and loading the Redis extension this way may interfere with Lucee’s own component class resolution.

But that is currently an inference, not something we have proven.

It probably deserves a separate bug report.

I’m mentioning it here only because it means the direct-classloading approach, although technically able to talk to Redis, does not look like a safe production workaround for us.

We reverted to Lucee’s built-in RAM cache and marked Redis as “do not use in production” in our internal documentation until this is understood.

Questions for the Lucee team

  • Is org.lucee:awssecretsmanager:1.12.758 expected to satisfy the org.lucee.aws-java-sdk-secretsmanager-all requirement?
    If so, we may be looking at a mismatch between the published bundle metadata and what redis.extension expects.

  • Is the AWS Secrets Manager dependency expected to be mandatory even when no Secrets Manager configuration is used?
    We may be misunderstanding how that dependency is intended to work, especially because an older thread suggested it had previously been made optional.

  • Is there a known-good way to install or obtain the required Secrets Manager bundle on Lucee 7/Jakarta?

  • Are bundles placed manually in WEB-INF/lucee-server/bundles/ expected to be discovered automatically, or is there additional registration/install state involved?
    Our patched bundle was still not resolved after clearing the Felix cache and restarting, so we may be missing a required installation step.

Separate classloading question

This may deserve another thread, but since we encountered it while looking for a workaround:

Is:

createObject("java", className, jarPaths)

supposed to use an isolated classloader?

If so, the ComponentPageRef.cpRef NullPointerException we observed after loading the Redis jars may be worth reporting separately.

We do not have enough knowledge of Lucee’s internal classloader hierarchy to determine whether this is a Lucee isolation issue or something caused by our approach, so I don’t want to overstate that conclusion.

Happy to provide more

We can provide, if useful:

  • complete startup logs;
  • the relevant Application.cfc cache configuration;
  • the original and patched AWS Secrets Manager jars/manifests;
  • a small self-contained redis-classload-test.cfm reproduction script.

And apologies again for the wall of text. :sweat_smile:

After three days of chasing this through Redis, OSGi, AWS jars, manifests, Felix caches and classloaders, the short version stopped being particularly short.

Happy summer Luceers!

Since I don’t like going to the beach in summer :sweat_smile:, I continued to investigate this problem and I think I found something interesting:

ObjectSave() / ObjectLoad() on a CFC instance produces a restored object whose ComponentPageRef is null, causing GetMetadata() — and potentially anything else that needs to resolve the component’s page — to fail on that specific instance.

This took me a while to get to, because at first everything pointed toward Redis, OSGi or Java classloading.

It apparently wasn’t any of them.

How I got here

The exact same NullPointerException involving GetMetadata() / ComponentPageRef.cpRef kept appearing across completely different libraries and loading mechanisms.

That eventually pushed me away from redis.extension, OSGi and classloading as the primary cause.

Here is the full trail:

# What I loaded How Result
1 redis.extension + org.mongodb.bson createObject( "java", class, jarPaths ) with explicit jar paths ComponentPageRef.cpRef NPE
2 Same two jars this.javaSettings.LoadPaths, reloadOnChange=false Identical NPE
3 Same two jars this.javaSettings.LoadPaths, reloadOnChange=true + watch Identical NPE
4 Same jars + AWS dependency via maven this.javaSettings.bundlePaths — real OSGi bundle loading rather than raw classpath Bundle never resolved; application failed to boot
5 Same jars + a hand-patched, byte-correct AWS bundle this.javaSettings.bundlePaths again, with the dependency supplied differently Same as #4 — bundle still did not resolve
6 Jedis — a completely unrelated Redis client, with no OSGi and no AWS dependency this.javaSettings.LoadPaths Identical ComponentPageRef.cpRef NPE
7 Nothing external at all — a Redis client hand-written using java.net.Socket / javax.net.ssl.SSLSocketFactory, with JDK-only classes No jars and no this.javaSettings whatsoever Still the identical NPE

Attempt #6 was particularly useful because Jedis shares essentially nothing with redis.extension.

Same Redis server, but different library, different implementation, no Lucee Redis extension, no OSGi and no AWS SDK dependency.

Yet I got the exact same failure.

Attempt #7 made things even stranger.

There was no external library left at all.

No jar.
No OSGi.
No this.javaSettings.
No custom classloader.

Just JDK classes talking directly to Redis over a socket.

And I still eventually hit:

java.lang.NullPointerException
Cannot invoke "lucee.runtime.component.ComponentPageRef.get(lucee.runtime.PageContext)"
because "this.cpRef" is null

At that point, “which Redis library am I using?” and “how am I loading it?” no longer seemed like useful questions.

So I started removing variables one by one.

Reducing it further

I created four small standalone tests, each using GetMetadata() on a CFC as a canary.

1. Creating a Java object

createObject( "java", "java.lang.String" );

Result: fine.

2. Real blocking network I/O

I connected to Redis, wrote a command and read the response directly inside a CFML script.

Result: fine.

3. Same network I/O through a WireBox singleton

I moved the exact same socket code into a WireBox singleton, persisted across requests.

Result: fine.

4. Serialize and restore a CFC

Then I removed Redis, networking, jars, Java settings and WireBox entirely.

I simply did this:

bean = new SomeBean();
bean.setName( "test" );

restored = ObjectLoad( ObjectSave( bean ) );

GetMetadata( restored );

And there it was.

Immediate reproduction of the same ComponentPageRef.cpRef NPE.

No Redis.
No OSGi.
No third-party library.
No network.
No singleton.

Just:

CFC
→ ObjectSave()
→ ObjectLoad()
→ GetMetadata()

Minimal reproduction

Bean:

component accessors="true" {
    property name="name" type="String";
}

Test:

bean = new SomeBean();
bean.setName( "test" );

restored = ObjectLoad( ObjectSave( bean ) );

GetMetadata( restored );

Result:

java.lang.NullPointerException

Cannot invoke
"lucee.runtime.component.ComponentPageRef.get(lucee.runtime.PageContext)"
because "this.cpRef" is null

A control test on a fresh CFC instance works normally:

fresh = new SomeBean();

GetMetadata( fresh );

And I can do both in the same request: the fresh instance works, while the deserialized instance fails.

So the problem appears to be scoped to the object returned by ObjectLoad(), rather than being some global corruption of Lucee’s component system.

What this seems to indicate

At least on my current Lucee build, an ObjectSave() / ObjectLoad() round trip appears to restore the CFC’s data but not all of the internal state required for it to behave as a normal component.

More specifically, ComponentPageRef appears to be missing or not reconstructed correctly.

That would explain why:

GetMetadata( restored )

fails while the same call on a newly-created instance succeeds.

It also changes how I interpret the original Redis issue.

Initially, I thought loading Redis-related Java classes was somehow corrupting Lucee’s component classloading.

That looked plausible because the error first surfaced while I was experimenting with redis.extension, and later with Jedis.

But the minimal example above reproduces the same failure without any Redis code at all.

So classloading now looks much less likely to be the root cause.

One part I still don’t fully understand

The first versions of this problem appeared while caching CFC instances through Redis.

redis.extension uses BSON-based serialization internally rather than ObjectSave() / ObjectLoad(), yet I saw the same ComponentPageRef.cpRef failure.

Jedis also produced the same symptom in my tests.

So there may be a broader issue around restoring CFC instances after serialization.

However, I don’t think I have enough evidence to claim that every external deserialization mechanism necessarily causes the same problem.

What I can reproduce independently and reliably is much narrower:

ObjectLoad( ObjectSave( cfc ) ) gives me a CFC instance on which GetMetadata() throws because ComponentPageRef.cpRef is null.

That seems like the useful starting point.

This also looks specific to Lucee 7, not a general ObjectSave/ObjectLoad behavior. Ran the exact same two-file repro (no app dependency at all) on two engines:

  • With 7.0.5.41 (this app’s engine) - bug reproduced, identical NPE
  • With 6.0.4.10 and 6.2.8.20 - clean, GetMetadata() on the restored instance succeeds

It looks like a regression bug in Lucee 7.

Environment

Same environment as in my original Redis report:

  • Lucee: 7.0.5.41 (works fine in 6.0.4.10 and 6.2.8.20)
  • CommandBox: 6.3.4+00883
  • Java 21 and 11
  • Ubuntu
  • CommandBox-managed Runwar/Undertow

I may of course be misunderstanding what ObjectSave() / ObjectLoad() is supposed to guarantee for CFCs, and I’d be very happy if this turned out to have a simple explanation.

But after the slightly absurd journey through Redis extensions, OSGi bundles, AWS dependencies, custom Java classloading, Jedis, raw sockets and finally a tiny reproduction with no external dependencies at all, this is the first thing I’ve found that consistently explains the symptom. :sweat_smile:

And yes, I know this is another rather long report — but these have been three fairly intense days of debugging. :sweat_smile:

I’m happy to provide a complete runnable repro app if useful.