Before submitting
Area
apps/server
Steps to reproduce
-
On Linux with systemd, install the t3 CLI globally through a Node version manager/package-manager setup where the t3 executable is a symlink to the package's real dist/bin.mjs entry point. This was reproduced with mise and a global npm package.
-
Run either:
or:
-
The command fails while trying to copy service-launcher.mjs into the managed runtime.
The published package does contain service-launcher.mjs next to the real dist/bin.mjs. The failure occurs because the lookup is based on the unresolved symlink invocation path.
Expected behavior
The CLI should resolve its packaged launcher relative to the real CLI entry point and install or update the background service successfully, regardless of whether the executable was invoked through a package-manager or version-manager symlink.
Actual behavior
The CLI looks for service-launcher.mjs beside the symlink in the global executable directory instead of beside the real dist/bin.mjs, then fails with BootServiceInstallError / ENOENT.
The relevant implementation sets cliEntryPath from HostProcessArguments[1] and derives launcherSourcePath using dirname(cliEntryPath). Node preserves the symlink used for invocation in process.argv[1], so the derived directory is not necessarily the package's real dist directory.
This appears to have been introduced with commit d3037064e6, which added the packaged service-launcher lookup. It is confirmed in 0.0.32-nightly.20260801.974, and current main still contains the unresolved lookup. Earlier update failures may have other causes; this report intentionally does not claim that earlier releases had this exact failure.
Impact
Major degradation or frequent failure
Version or commit
Confirmed in 0.0.32-nightly.20260801.974; also present on current main
Environment
Linux with systemd, Node.js 24.18.0, mise-managed Node installation, globally installed npm CLI
Logs or stack traces
BootServiceInstallError: Could not set up the T3 Code background service.
[cause]: PlatformError: NotFound: FileSystem.readFile (<global-executable-directory>/service-launcher.mjs)
[cause]: Error: ENOENT: no such file or directory, open '<global-executable-directory>/service-launcher.mjs'
No user-specific paths are required to reproduce the issue. The launcher is present beside the real package entry point; only the symlink-relative lookup is missing it.
Workaround
Resolve the CLI symlink before invoking the command so process.argv[1] is the real package entry point:
node "$(realpath "$(command -v t3)")" service install
For an existing service that needs an update or repair:
node "$(realpath "$(command -v t3)")" service update
Both commands avoid adding or copying files into the global executable directory.
Suggested fix
Resolve cliEntryPath through the filesystem before deriving launcherSourcePath, or derive the launcher location from a module-relative URL that refers to the packaged dist directory. A regression test should invoke the CLI entry through a symlink and verify that installation reads the launcher beside the symlink target.
Before submitting
Area
apps/server
Steps to reproduce
On Linux with systemd, install the
t3CLI globally through a Node version manager/package-manager setup where thet3executable is a symlink to the package's realdist/bin.mjsentry point. This was reproduced with mise and a global npm package.Run either:
or:
The command fails while trying to copy
service-launcher.mjsinto the managed runtime.The published package does contain
service-launcher.mjsnext to the realdist/bin.mjs. The failure occurs because the lookup is based on the unresolved symlink invocation path.Expected behavior
The CLI should resolve its packaged launcher relative to the real CLI entry point and install or update the background service successfully, regardless of whether the executable was invoked through a package-manager or version-manager symlink.
Actual behavior
The CLI looks for
service-launcher.mjsbeside the symlink in the global executable directory instead of beside the realdist/bin.mjs, then fails withBootServiceInstallError/ENOENT.The relevant implementation sets
cliEntryPathfromHostProcessArguments[1]and deriveslauncherSourcePathusingdirname(cliEntryPath). Node preserves the symlink used for invocation inprocess.argv[1], so the derived directory is not necessarily the package's realdistdirectory.This appears to have been introduced with commit
d3037064e6, which added the packaged service-launcher lookup. It is confirmed in0.0.32-nightly.20260801.974, and currentmainstill contains the unresolved lookup. Earlier update failures may have other causes; this report intentionally does not claim that earlier releases had this exact failure.Impact
Major degradation or frequent failure
Version or commit
Confirmed in
0.0.32-nightly.20260801.974; also present on currentmainEnvironment
Linux with systemd, Node.js 24.18.0, mise-managed Node installation, globally installed npm CLI
Logs or stack traces
No user-specific paths are required to reproduce the issue. The launcher is present beside the real package entry point; only the symlink-relative lookup is missing it.
Workaround
Resolve the CLI symlink before invoking the command so
process.argv[1]is the real package entry point:node "$(realpath "$(command -v t3)")" service installFor an existing service that needs an update or repair:
node "$(realpath "$(command -v t3)")" service updateBoth commands avoid adding or copying files into the global executable directory.
Suggested fix
Resolve
cliEntryPaththrough the filesystem before derivinglauncherSourcePath, or derive the launcher location from a module-relative URL that refers to the packageddistdirectory. A regression test should invoke the CLI entry through a symlink and verify that installation reads the launcher beside the symlink target.