Following up on the warmup thread (docker-build-warmup-behaviour/17547) and LDEV-6472, there is a separate related issue on clean startup when extensions install automatic event gateways (e.g. Quartz Scheduler).
Disclaimer: I used AI to assist in this debugging/report
During initial context initialization, Lucee throws a NullPointerException:
ERROR [server.gateway] Cannot invoke "lucee.commons.io.res.Resource.getCanonicalPath()" because "a" is null
lucee.runtime.exp.NativeException: Cannot invoke "lucee.commons.io.res.Resource.getCanonicalPath()" because "a" is null
at lucee.runtime.config.ConfigImpl.getBaseComponentPageSource(ConfigImpl.java:2296)
at lucee.runtime.config.ConfigWebImpl.getBaseComponentPageSource(ConfigWebImpl.java:464)
at lucee.runtime.config.ConfigWebHelper.getBaseComponentPage(ConfigWebHelper.java:254)
at lucee.runtime.config.ConfigWebHelper.getBaseComponentInstance(ConfigWebHelper.java:277)
at lucee.runtime.config.ConfigWebImpl.getBaseComponentInstance(ConfigWebImpl.java:1614)
at lucee.runtime.ComponentImpl.init(ComponentImpl.java:480)
at org.lucee.extension.quartz.quartzgateway_cfc$cf.initComponent(/org/lucee/extension/quartz/QuartzGateway.cfc)
at org.lucee.extension.quartz.quartzgateway_cfc$cf.newInstance(/org/lucee/extension/quartz/QuartzGateway.cfc:1)
at lucee.runtime.ComponentPageImpl.call(ComponentPageImpl.java:161)
at lucee.runtime.gateway.GatewayEngineImpl.getCFC(GatewayEngineImpl.java:440)
at lucee.runtime.gateway.GatewayEngineImpl.autoStart(GatewayEngineImpl.java:210)
at lucee.runtime.config.ConfigFactoryImpl.newInstanceWeb(ConfigFactoryImpl.java:229)
Root Causes
Tracing through the Lucee 7 codebase, this is caused by three compounding issues:
1. Inverted Lifecycle Order (ConfigFactoryImpl.java:345 vs 348)
345: load(config, root, false, doNew, essentialOnly);
// ↳ _loadExtensionBundles() downloads/installs extensions -> registers Quartz gateway
...
348: createContextFiles(configDir, config, doNew);
// ↳ line 4030 calls ComponentFactory.deploy() -> extracts Component.cfc from core.lco
- In
load()(line 345), Lucee installs extension bundles. Extensions registering event gateways get auto-started whennewInstanceWeb()executes((GatewayEngineImpl) configWeb.getGatewayEngine()).autoStart(). QuartzGateway.cfcextendsComponent.cfc.- However,
ComponentFactory.deploy()(which extracts base components likeComponent.cfcfromcore.lcointocomponents/org/lucee/cfml/) is not called untilcreateContextFiles()at line 348. - Because the gateway starts before core components are unpacked, the
Component.cfclookup fails.
2. Typo in ConfigImpl.java:2294-2296
When Lucee attempts to format the component lookup error message, it hits this block:
Resource a = m.getArchive();
String archive = m.getStrArchive();
if (p != null) { // <-- Bug: checks physical resource 'p' instead of archive 'a'
archive = a.getCanonicalPath() + " (" + m.getStrArchive() + ")"; // Throws NPE because 'a' is null!
}
- When resolving against a physical-only mapping (such as
/lucee-server/which has"primary": "physical"and"archive": ""), archiveaisnull. - The code checks
if (p != null)instead ofif (a != null). - Because
pexists, it callsa.getCanonicalPath()onnull, throwing the rawNullPointerExceptionand completely masking the underlying missing component error.
3. Unnecessary Gateway Auto-Start in Warmup Mode
- In
LUCEE_ENABLE_WARMUP=true(or any headless build mode), external resources (databases, message queues, mail servers) do not exist. - Auto-starting event gateways during warmup is counter-productive: it spawns background threads and attempts connections against non-existent infrastructure before immediately shutting down.
Suggested Fixes
- Suppress Gateway Auto-Start During Warmup: When
LUCEE_ENABLE_WARMUP=true, skip((GatewayEngineImpl) configWeb.getGatewayEngine()).autoStart()inConfigFactoryImpl.java:229. Gateways have no purpose running during a build warmup pass. - Fix NPE in
ConfigImpl.java:2296: Changeif (p != null)toif (a != null). - Ensure Core Components Deploy First: Run
ComponentFactory.deploy()before_loadExtensionBundles()so base components exist whenever extensions load.