FreeBSD www/node24 fails to build: missing SQLite session extension
TL;DR: www/node24 on FreeBSD unconditionally links against the ports databases/sqlite3 library instead of its own bundled copy, and databases/sqlite3 doesn’t expose a build option for SQLite’s session extension. Node 22+’s built-in node:sqlite module needs that extension to implement changesets/patchsets, so the link fails. There’s currently no port-level option to fix this on either side.
Environment
- FreeBSD 14.4-RELEASE (mid-upgrade from 13.5 to 15.1)
- Building via
poudriereagainst a current ports tree checkout www/node24-24.21.0databases/sqlite3(ports default build, no custom OPTIONS)
The failure
The build fails at the final link step of the node binary itself, with six undefined symbols, all from SQLite’s session/changeset API:
ld: error: undefined symbol: sqlite3session_delete
>>> referenced by node_sqlite.cc
>>> node_sqlite.o:(node::sqlite::DatabaseSync::DeleteSessions())
ld: error: undefined symbol: sqlite3session_create
ld: error: undefined symbol: sqlite3session_attach
ld: error: undefined symbol: sqlite3changeset_apply
ld: error: undefined symbol: sqlite3session_changeset
ld: error: undefined symbol: sqlite3session_patchset
c++: error: linker command failed with exit code 1
gmake: *** [Makefile:143: node] Error 2
Root cause
Node.js 22 introduced a built-in node:sqlite module. Its session-tracking support (creating sessions, generating changesets and patchsets, applying changesets) is a thin wrapper directly around SQLite’s session extension — the sqlite3session_* and sqlite3changeset_* family of functions. That extension is not part of a default SQLite build; it has to be compiled in explicitly with SQLITE_ENABLE_SESSION and SQLITE_ENABLE_PREUPDATE_HOOK.
Upstream Node solves this by bundling its own copy of SQLite (under deps/sqlite/) built with those flags. The FreeBSD port takes a different approach: it hardcodes --shared-sqlite and actively deletes the bundled source tree during build, forcing Node to link against the system/ports SQLite instead:
# www/node24/Makefile
libsqlite3.so:databases/sqlite3 \
--shared-sqlite \
@${RM} -r ${WRKSRC}/deps/sqlite/
There’s no fallback and no build knob to use the bundled copy instead — it’s unconditional. Meanwhile, databases/sqlite3‘s own OPTIONS_DEFINE list has no session-extension toggle at all:
OPTIONS_DEFINE= EXAMPLES TCL THREADS
OPTIONS_DEFINE+= ARMOR DBPAGE DBSTAT DIRECT_READ DQS EXTENSION FTS3_TOKEN \
FTS4 FTS5 LIKENOTBLOB MEMMAN METADATA NORMALIZE NULL_TRIM RBU SECURE_DELETE \
SORT_REF STATIC STMT TRUSTED_SCHEMA UNKNOWN_SQL UNLOCK_NOTIFY \
UPDATE_LIMIT URI URI_AUTHORITY
(EXTENSION here refers to SQLite’s dlopen-based loadable-extension support — a different feature entirely from the session extension.)
The result: as shipped, these two ports are incompatible for this one specific, still-experimental Node feature. This isn’t a local misconfiguration — there’s no options combination on either port that resolves it.
Impact assessment
Before treating this as blocking, it’s worth checking what actually depends on node24 on the affected system:
$ pkg query %rn node24
yarn-node24
rubygem-execjs
Neither of these touches node:sqlite. yarn-node24 just needs a working Node runtime to execute JavaScript; rubygem-execjs is a generic “run some JS via whatever runtime is available” shim for Ruby. Unless something on a given system explicitly calls the new node:sqlite session/changeset APIs, this failure is safe to defer — retry everything else and come back to it separately.
Possible fixes (not yet applied upstream)
- Add a build option to
databases/sqlite3exposingSQLITE_ENABLE_SESSION/SQLITE_ENABLE_PREUPDATE_HOOK. Safe in principle — the session extension only adds new exported symbols, it doesn’t change existing SQLite behavior for other consumers of the library. - Add a knob to
www/node24to fall back to its bundled SQLite copy instead of unconditionally forcing--shared-sqlite, at least when the system SQLite lacks the session extension. - As a local, unofficial workaround: patch
databases/sqlite3‘sCFLAGSin a private ports overlay to add the two-Dflags directly, without waiting for an upstream options change.
Status
No existing FreeBSD Bugzilla report was found covering this exact combination at the time of writing. Filing one against www/node24 (category “Individual Port(s)”) would be the appropriate next step for anyone who needs this resolved rather than deferred.
Written up during a FreeBSD 13.5 → 15.1 upgrade using poudriere for package rebuilds. Details (port revisions, exact error text) reflect the ports tree snapshot in use at the time and may have changed since.