Lucee 7.1.0.204 / Mail Extension 1.1.0.6 — spooled cfmail silently dropped (NotSerializableException: sun.nio.cs.UTF_8)

After upgrading from 7.0.4.34 to 7.1.0.204 and installing Mail Extension 1.1.0.6, every cfmail with spooling enabled
(the default) is silently lost. cfmail returns without error, nothing reaches the SMTP server, and application.log
shows on each send:

“ERROR”,…,“lucee.runtime.spooler.SpoolerEngineImpl”,“java.io.NotSerializableException: sun.nio.cs.UTF_8”
“ERROR”,…,“lucee.runtime.spooler.SpoolerEngineImpl”,“writing aborted; java.io.NotSerializableException:
sun.nio.cs.UTF_8”

(sun.nio.cs.ISO_8859_1 when charset=“iso-8859-1” is used.) The spooler task appears to hold a java.nio.charset.Charset
field, which is not Serializable, so writing the .tsk file fails and the mail is discarded.

Sending with spoolenable=“false” works normally, as does disabling spooling in Admin → Services → Mail.

Environment: Windows 11, Tomcat 11.0.18, Lucee 7.1.0.204, Mail Extension 1.1.0.6, SMTP via Postmark (TLS, port 587).
Reproducible with a one-line on a fresh install with default settings.

Impact: production ticketing site lost all purchase confirmation / eTicket emails for ~14 hours with no error surfaced
to the application. Suggest also that spooler serialization failures be raised to the caller or at least logged to
mail.log rather than only application.log.

1 Like

You’ve diagnosed it correctly. With spooling on, each task is persisted to a .tsk file via Java serialization before it runs. The mail task now holds a java.nio.charset.Charset (which isn’t Serializable), so the write throws NotSerializableException and the spooler currently only logs it to application.log rather than surfacing it — hence the mail is dropped with no error to the application. Your spoolenable="false" / disable-spooling workarounds line up with that.

I’ve opened a ticket to track it, covering both the non-serializable field in the Mail extension and your very fair point that a spooler persistence failure should be raised to the caller (or at least logged to mail.log) instead of silently discarding the message:

https://luceeserver.atlassian.net/browse/LDEV-6455

We’ll address it there — feel free to follow along or add anything further on the ticket.

@team_ims Fixed. Some background: as part of Lucee 7.1 the mail functionality (cfmail, cfimap, etc.) was moved out of core into a standalone Mail extension. In that move the code stopped using Lucee’s own (serializable) CharSet class and switched to the JDK’s java.nio.charset.Charset — which isn’t Serializable. So when spooling was on, the spooler couldn’t persist the task to disk (the NotSerializableException you saw) and the mail was dropped.

Two fixes have landed:

  • Mail extension 1.1.0.8-RC — the charset is now held in a serializable wrapper again, so spooled cfmail persists and sends normally. Available on the extension store / Maven now.
  • Lucee 7.1 — the spooler no longer silently discards a task it can’t persist: it runs it inline and logs the failure at ERROR instead. This is bundled by default and will merge forward to 8.

Tracked in LDEV-6455. Updating to Mail extension 1.1.0.8-RC resolves it — thanks for the excellent, detailed report.

@micstriit I’m not seeing 1.1.0.8-RC completely fix this issue, though setting spoolenable="false" is a workaround. You mention updating the mail extension fixes it, but also list “Two fixes have landed” - do we need an update to 7.1.0.204 to work in conjunction with the new mail ext. to make this work? If so, which SNAP as it doesn’t appear to be listed in the ticket?

1 Like

Hi, is 7.1.0.204 safe from this issue now? I see it’s still listed as the latest version.

I ran into this issue. Claude Fable 5.1 wrote this up for me:

Upgrading a 7.0.x server to 7.1.0.204 through the normal patch path (Admin → Services → Update, or dropping the .lco into lucee-server/deploy/) leaves the server without the Mail Extension. Every template that contains then fails to compile:

Error: undefined tag [cfmail], at [2353:20] in [components/flyer.cfc]

Nothing is logged about the extension, so the first sign is the application error log filling up. On our production node that was ~2,100 errors in the six minutes before we rolled back.

I believe this is a small gap in how required extensions are handled on a minor update, not a download problem. Details below.

Environment

  • Lucee 7.0.4.34 → 7.1.0.204 via .lco
  • Apache Tomcat 11.0.15, OpenJDK 21.0.9, Amazon Linux 2023, installed with the 7.0.1.100 Linux installer (/opt/lucee), so lucee-7.0.1.100.jar is the bundled jar and 7.0.4.34 was itself a patch
  • No lucee.extensions env var, no lucee.extensions.config.only, lucee.extensions.install unset (defaults to true)
  • Outbound HTTPS to repo1.maven.org, maven-central.storage-download.googleapis.com and ext.lucee.org all work from the node

What the logs show

deploy.log for the update, in full:

“INFO”,“ControllerThread:1349”,“09/01/2026”,“23:54:53”,“”,“deploy handler”,“found [/opt/lucee/tomcat/lucee-server/deploy/7.1.0.204.lco] in deploy folder”
“INFO”,“ControllerThread:1349”,“09/01/2026”,“23:54:53”,“”,“Update-Engine”,“Installing Lucee [7.1.0.204] (previous version was [7.0.4.34] )”
“INFO”,“ControllerThread:1349”,“09/01/2026”,“23:54:56”,“”,“request”,“No new extension available to add to local extension directory”

No “Detected Extensions to install”, no “Installing extension”, no error. lucee-server/context/mvn/cache/ was created at 23:54 and stayed empty. lucee-server/context/required-extension was written as 7.1.0.204-1787326368000.

The manifest does declare it

$ unzip -p 7.1.0.204.lco META-INF/MANIFEST.MF # Require-Extension, unwrapped
org.lucee:mysql-jdbc-extension:9.6.0
org.lucee:mssql-jdbc-extension:13.2.1
org.lucee:postgresql-jdbc-extension:42.7.9
org.lucee:administrator-extension:1.0.0.7
org.lucee:documentation-extension:1.0.0.6
org.lucee:s3-extension:2.0.3.1
org.lucee:pdf-extension:2.0.1.0
org.lucee:image-extension:3.0.0.9
org.lucee:esapi-extension:3.0.0.14
org.lucee:scheduler-classic-extension:1.0.0.1
org.lucee:compress-extension:2.1.0.2-SNAPSHOT
org.lucee:mail-extension:1.1.0.6
org.lucee:ftp-extension:1.0.0.5-RC

mail-extension and ftp-extension are the two entries that are new relative to the 7.0.5.41 manifest. The .lco contains no .lex files, so nothing is bundled either.

Why it is skipped

core/src/main/java/lucee/runtime/engine/CFMLEngineImpl.java (7.1 branch), the required-extensions block. For a minor update (line 476, updateInfo.updateType == ConfigFactory.NEW_MINOR), each required extension that is not already installed goes through this at lines 496–497:

Version since = ed.getSince();
if (since == null || updateInfo.oldVersion == null || !OSGiUtil.isNewerThan(since, updateInfo.oldVersion)) continue; // not installed we do not update

ExtensionDefintion.getSince() reads the since parameter of the manifest entry. None of the entries carry one (grep -c since MANIFEST.MF → 0), so for a missing extension since == null and the loop continues silently. The NEW_FRESH branch a few lines up installs the whole list, which is why fresh installs and the full lucee.jar (which bundles the .lex) are fine and only the patch path is affected.

Same applies to ftp-extension / cfftp, we just don’t use it.

Suggested fix

Either of:

  1. Add since to the entries that moved out of the core, e.g. org.lucee:mail-extension:1.1.0.6;since=7.1.0.0 and org.lucee:ftp-extension:1.0.0.5-RC;since=7.1.0.0. RHExtension.toExtensionDefinition() applies name=value tokens after the Maven coordinate to the definition, so this parses today.
  2. Bundle the Mail (and FTP) .lex inside the .lco so deployBundledExtension picks them up on NEW_MINOR.

And, whichever is chosen, a log line at INFO when a required extension is skipped for lack of since would have made this a two-minute diagnosis instead of a rollback.

Workaround for anyone hitting this

Put mail-extension-.lex (Maven Central or https://ext.lucee.org/mail-extension-.lex) into lucee-server/deploy/ together with the .lco, so it installs when the new core boots. Note the extension in the manifest, 1.1.0.6, also has the spooled-mail serialisation bug from Lucee 7.1.0.204 / Mail Extension 1.1.0.6 — spooled cfmail silently dropped (NotSerializableException: sun.nio.cs.UTF_8) (LDEV-6455), so you probably want 1.1.0.8 or later once it is out of RC.

7.1.1.3-SNAPSHOT

References:
Commit 032ca3d
Commit 53b436a

However, that’s for logging failure as ERROR.

You might need to provide more detail re: “not seeing 1.1.0.8-RC completely fix this issue”?

Upgrading 7.1.0.204 to use the Mail 1.1.0.8-RC results in no mail being sent and logs the error below. Upgrading to Lucee 7.1.1.3-SNAPSHOT, along with the Mail 1.1.0.8-RC also presents same issue. Setting mailSpoolEnable to false is a temp workaround in dev:

"ERROR","Thread-145","09/02/2026","22:54:43","https://bh.dev.local:443","lucee.runtime.spooler.SpoolerEngineImpl","org.lucee.extension.mail.spooler.MailSpoolerTaskjava.lang.ClassNotFoundException: org.lucee.extension.mail.spooler.MailSpoolerTask
	at java.base/java.net.URLClassLoader.findClass(URLClassLoader.java:377)
	at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:557)
	at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:490)
	at org.apache.felix.framework.BundleWiringImpl.doImplicitBootDelegation(BundleWiringImpl.java:1811)
	at org.apache.felix.framework.BundleWiringImpl.tryImplicitBootDelegation(BundleWiringImpl.java:1740)
	at org.apache.felix.framework.BundleWiringImpl.searchDynamicImports(BundleWiringImpl.java:1693)
	at org.apache.felix.framework.BundleWiringImpl.findClassOrResourceByDelegation(BundleWiringImpl.java:1569)
	at org.apache.felix.framework.BundleWiringImpl.access$300(BundleWiringImpl.java:79)
	at org.apache.felix.framework.BundleWiringImpl$BundleClassLoader.loadClass(BundleWiringImpl.java:1976)
	at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:490)
	at java.base/java.lang.Class.forName0(Native Method)
	at java.base/java.lang.Class.forName(Class.java:547)
	at java.base/java.io.ObjectInputStream.resolveClass(ObjectInputStream.java:745)
	at lucee.runtime.converter.JavaConverter$ObjectInputStreamImpl.resolveClass(JavaConverter.java:140)
	at java.base/java.io.ObjectInputStream.readNonProxyDesc(ObjectInputStream.java:1912)
	at java.base/java.io.ObjectInputStream.readClassDesc(ObjectInputStream.java:1785)
	at java.base/java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:2101)
	at java.base/java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1620)
	at java.base/java.io.ObjectInputStream.readObject(ObjectInputStream.java:487)
	at java.base/java.io.ObjectInputStream.readObject(ObjectInputStream.java:445)
	at lucee.runtime.spooler.SpoolerEngineImpl.getTask(SpoolerEngineImpl.java:210)
	at lucee.runtime.spooler.SpoolerEngineImpl.getTaskByName(SpoolerEngineImpl.java:198)
	at lucee.runtime.spooler.SpoolerEngineImpl$SpoolerThread.run(SpoolerEngineImpl.java:485)
1 Like

Confirmed same is happening for me with those exact same Lucee/Mail versions. Setting mailSpoolEnable/async to false is not a good workaround for production, so my workaround for now is to NOT upgrade to 7.1. :frowning:

1 Like

Throwing one more voice behind this. We just got bit by this on a test platform while testing our migration from 6 to 7.1.

On test platform we can use spoolenable false ok, but that won’t be sustainable for production.

While troubleshooting I also noticed that the Mail link is now missing from the Lucee admin? We use a text config so we don’t need it, but I think that might be a regression as well.

1 Like

Same - we had to forget the server in Commandbox and add the Mail 1.1.0.8-RC extension via .env file.

To confirm, I’ve also lost the Mail link in Lucee admin after installing the new Mail Extension on 7.1.0.204

2 Likes

No mail link on 1.1.0.7 also. The install and upgrade UI returned in 1.1.0.7.

1 Like

I have been digging and digging in on this. I have 2 servers dev & production. Email working on production.

I finally, after hours of trying thing, discovered that I did an inline upgrade on prod and a clean install on dev. The gist is that production still had the lucee-7.0.1.100.jar, while the clean install had 7.1.0.204. Downgrading the jar on development helped a lot.

I still can’t see “Mail” in the “Services” area of settings.
I am sometimes generating “jakarta.mail.Provider: com.sun.mail.imap.IMAPProvider not a subtype” on commands
I can send mail with the spoolenable=“false” hack, but don’t really want to re-write my code on every site over a temp fix.

1 Like

OK, the “jakarta.mail.Provider: com.sun.mail.imap.IMAPProvider not a subtype” error we ran down to odd spacing in parameters:

<cfimap connection="myconnection"> works. <cfimap connection = "myconnection"> throws an error. Frustrating, because that did not matter in the past.

This will get you the Mail UI
lucee/admin/index.cfm?action=services.mail

The 1.1.0.8-RC does NOT fix the issue. I tried 1.1.0.7 also.

1 Like

Here is the issue on Jira

https://luceeserver.atlassian.net/issues?jql=labels%20%3D%20smtp&selectedIssue=LDEV-6455

1 Like

Fixed in 7.1.1.4-SNAPSHOT — tracked in LDEV-6455.

Quick recap of what was going on, since there were really two bugs on the same serialize/deserialize round-trip:

  1. Write side (NotSerializableException: sun.nio.cs.UTF_8) — the mail extension’s spool task held a non-serializable java.nio.charset.Charset, so the spooler couldn’t even persist it. Fixed in Mail Extension 1.1.0.8.
  2. Read side (ClassNotFoundException: org.lucee.extension.mail.spooler.MailSpoolerTask) — once the task could be persisted, the spooler read it back with the core class loader, which can’t see classes that now live in the mail extension (since mail moved out of core into an extension). So the .tsk file was written, failed to load, and the mail was silently dropped.

The core fix (in 7.1.1.4-SNAPSHOT) makes the spooler resolve an extension-provided task class on demand against the currently installed extension when the core loader can’t — so a spooled cfmail is read back and sent correctly. It’s done this way (rather than remembering the class loader that wrote the task) precisely because spooling is meant to survive restarts and extension updates, where the original loader no longer exists.

This is now covered by test cases in the Lucee test suite — both an end-to-end spooled-delivery test and a direct ObjectSave/ObjectLoad serialization round-trip of the real mail spool task — so the write side and the read side are guarded against regressions going forward.

What to run: Lucee 7.1.1.4-SNAPSHOT or newer with Mail Extension 1.1.0.8 or newer, and keep mail spooling enabled. Earlier combinations on 7.1 (1.1.0.6, or 1.1.0.8-RC on older core) are the ones that drop spooled mail.

Thanks for the detailed reports and stack traces — they made this straightforward to pin down.

4 Likes