You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Two symptoms that turned out to be the same root cause:
The background service (t3code.service, systemd) was supposed to run independently of the desktop app, but broke as soon as the desktop app closed — it ended up depending on the desktop app staying open to work at all.
Every coding-agent provider (Claude Code, Codex, Cursor, Grok) showed as unavailable on every single launch.
Diagnosis
Both symptoms trace back to node-pty never getting a working native binary in the pinned runtime at runtime/versions/0.0.33/node_modules/node-pty/. There was no prebuilds/linux-x64/ directory (expected — node-pty@1.1.0 only ships darwin-{arm64,x64} and win32-{arm64,x64} prebuilds, same gap as #2621 and #6012), and no local build/Release/pty.node either, meaning the node-gyp rebuild fallback that's supposed to compile it locally on Linux never ran.
The reason the fallback never ran: the user's ~/.npmrc had allow-scripts=<some-other-package>, npm's script-allowlist config (npm 12, RFC npm/rfcs#868). Under that policy npm silently skips lifecycle scripts for any package not on the allowlist — node-pty's install script (node scripts/prebuild.js || node-gyp rebuild) and msgpackr-extract's were both silently skipped during ensurePinnedRuntimeInstalled's npm install --prefix <staging> t3@<version> (apps/server/src/cloud/pinnedRuntime.ts). npm only emits a low-visibility npm warn install-scripts ... blocked because they are not covered by allowScripts — nothing in pinnedRuntime.ts surfaces or checks for this, so the install reports success (sentinel written, t3code.service starts) with a permanently broken PTY backend.
From there:
apps/server/src/terminal/NodePtyAdapter.ts's make throws NodePtyModuleLoadError on every PTY attempt. Provider health checks (Claude Agent CLI, Grok CLI, etc.) go through a PTY and failed on every launch with errorTag: 'PlatformError'.
The systemd unit (Restart=always, RestartSec=5) crash-looped roughly every 6 seconds as the child process kept dying. Each restart cycle also tore down and re-established the relay tunnel (CloudManagedEndpointRuntime.reconcileConfig / AgentAwarenessRelay) — visible as repeating Relay client process started; waiting for tunnel connection → Relay client stopped pairs in boot-service.log, instead of a single stable connection. After 6 restarts inside StartLimitIntervalSec=300, systemd gave up: t3code.service sat in failed (start-limit-hit).
The desktop app runs its own separate local server instance (server.mode: "desktop") that never went through this broken pinned-runtime install, so it kept working — which is why the background service appeared to depend on the desktop app being open. desktop.trace.ndjson had zero NodePtyModuleLoadError occurrences the whole session, vs. 37 in boot-service.log.
This is a variant of #2621 / #6012 (missing Linux prebuild forces a local compile) but with a different trigger for the compile never happening: not a missing toolchain (this machine had make/gcc/g++/python3 present throughout), but npm's allow-scripts policy silently no-op'ing the install script with only a warning, which pinnedRuntime.ts never checks for.
Steps to reproduce
On Linux, set a restrictive allow-scripts policy in ~/.npmrc that does not cover node-pty or msgpackr-extract, e.g.:
allow-scripts=some-other-package
Install or update T3 Code so ensurePinnedRuntimeInstalled runs npm install --prefix <staging> --no-fund --no-audit t3@<version>.
The install exits 0 and the sentinel gets written, but node_modules/node-pty has no build/ and no prebuilds/linux-x64/ — the install script was silently skipped (npm warn install-scripts ... blocked because they are not covered by allowScripts).
Start t3code.service. It crash-loops on NodePtyModuleLoadError every RestartSec interval until StartLimitBurst is hit and the unit fails permanently. Provider health checks fail every launch. Relay tunnel connects/disconnects in lockstep with the crash loop.
Version
0.0.33 (also confirmed still present on main as of this report — node-pty is unchanged in apps/server/package.json, still a plain dependencies entry with no Linux prebuild vendoring or allow-scripts detection)
Environment
Linux x64 (Arch, kernel 7.1.8-arch1-3), Node v26.7.0, npm 12.0.2, systemd user service (t3code.service)
Evidence
# boot-service.log — repeating crash loop
[20:30:50.997] WARN (#90): Claude Agent CLI health check failed.
{ errorTag: 'PlatformError' }
[20:30:51.005] WARN (#106): Grok CLI health check failed.
{ errorTag: 'PlatformError' }
[20:30:51.029] ERROR (#5): NodePtyModuleLoadError: Failed to load node-pty for linux-x64.
at catch (.../node_modules/t3/dist/NodePtyAdapter-DVcUyBhi.mjs:89:21)
[cause]: Error: Failed to load native module: pty.node, checked: build/Release, build/Debug, prebuilds/linux-x64:
Error: Cannot find module './prebuilds/linux-x64//pty.node'
[service-launcher] Active child exited unexpectedly (1).
# ...repeats every ~6-7s, 37 times total before systemd gives up
# systemctl --user status t3code.service
Active: failed (Result: start-limit-hit)
Aug 18 22:13:42 desktop systemd[717]: Start request repeated too quickly.
Aug 18 22:13:42 desktop systemd[717]: t3code.service: Failed with result 'start-limit-hit'.
# relay cycling in lockstep with the crash loop (boot-service.log)
[21:00:19.389] INFO (#62): Relay client process started; waiting for tunnel connection
[21:00:20.094] INFO (#182): Relay client stopped
[21:00:26.619] INFO (#62): Relay client process started; waiting for tunnel connection
[21:00:27.461] INFO (#182): Relay client stopped
# ...repeats
# npm install-scripts ls --prefix runtime/versions/0.0.33
2 packages have install scripts blocked because they are not covered by allowScripts:
msgpackr-extract@3.0.4 (install: node-gyp rebuild)
node-pty@1.1.0 (install: node-gyp rebuild)
Remote server update fails on Linux when node-pty must compile (no linux-x64 prebuild; opaque error) #6012 (open) — same missing-prebuild gap causing opaque failures on remote/self-update. Not a duplicate: that report's compile failed due to a missing g++-11 package; this report's compile was silently skipped entirely by npm's allow-scripts policy, with a full toolchain present and available the whole time. Different trigger, same missing-prebuild root cause underneath, and neither report's fix (documenting compiler prerequisites) would have caught this one, since no compiler was ever invoked here.
This writes an allowScripts block into that runtime's own package.json (scoped to just this pinned runtime, the user's global ~/.npmrc policy was left untouched), lets node-gyp rebuild run for real, and it compiled cleanly with the toolchain already on the machine. Service has been stable since (relay tunnel connected, no PTY errors, no restarts).
Suggested upstream fixes:
ensurePinnedRuntimeInstalled (apps/server/src/cloud/pinnedRuntime.ts) could check npm install-scripts ls (or parse stderr for the blocked because they are not covered by allowScripts warning) after install and surface it as an actionable PinnedRuntimeInstallError, instead of writing the success sentinel over a silently-broken native module.
NodePtyAdapter.make's failure currently just crash-loops the whole process (via Effect.orDie); consider degrading providers that need a PTY instead of taking the whole background service down, so a broken PTY backend doesn't also kill the relay connection.
What happened
Two symptoms that turned out to be the same root cause:
t3code.service, systemd) was supposed to run independently of the desktop app, but broke as soon as the desktop app closed — it ended up depending on the desktop app staying open to work at all.Diagnosis
Both symptoms trace back to
node-ptynever getting a working native binary in the pinned runtime atruntime/versions/0.0.33/node_modules/node-pty/. There was noprebuilds/linux-x64/directory (expected —node-pty@1.1.0only shipsdarwin-{arm64,x64}andwin32-{arm64,x64}prebuilds, same gap as #2621 and #6012), and no localbuild/Release/pty.nodeeither, meaning thenode-gyp rebuildfallback that's supposed to compile it locally on Linux never ran.The reason the fallback never ran: the user's
~/.npmrchadallow-scripts=<some-other-package>, npm's script-allowlist config (npm 12, RFC npm/rfcs#868). Under that policy npm silently skips lifecycle scripts for any package not on the allowlist —node-pty'sinstallscript (node scripts/prebuild.js || node-gyp rebuild) andmsgpackr-extract's were both silently skipped duringensurePinnedRuntimeInstalled'snpm install --prefix <staging> t3@<version>(apps/server/src/cloud/pinnedRuntime.ts). npm only emits a low-visibilitynpm warn install-scripts ... blocked because they are not covered by allowScripts— nothing inpinnedRuntime.tssurfaces or checks for this, so the install reports success (sentinel written,t3code.servicestarts) with a permanently broken PTY backend.From there:
apps/server/src/terminal/NodePtyAdapter.ts'smakethrowsNodePtyModuleLoadErroron every PTY attempt. Provider health checks (Claude Agent CLI, Grok CLI, etc.) go through a PTY and failed on every launch witherrorTag: 'PlatformError'.Restart=always,RestartSec=5) crash-looped roughly every 6 seconds as the child process kept dying. Each restart cycle also tore down and re-established the relay tunnel (CloudManagedEndpointRuntime.reconcileConfig/AgentAwarenessRelay) — visible as repeatingRelay client process started; waiting for tunnel connection→Relay client stoppedpairs inboot-service.log, instead of a single stable connection. After 6 restarts insideStartLimitIntervalSec=300, systemd gave up:t3code.servicesat infailed (start-limit-hit).server.mode: "desktop") that never went through this broken pinned-runtime install, so it kept working — which is why the background service appeared to depend on the desktop app being open.desktop.trace.ndjsonhad zeroNodePtyModuleLoadErroroccurrences the whole session, vs. 37 inboot-service.log.This is a variant of #2621 / #6012 (missing Linux prebuild forces a local compile) but with a different trigger for the compile never happening: not a missing toolchain (this machine had
make/gcc/g++/python3present throughout), but npm'sallow-scriptspolicy silently no-op'ing the install script with only a warning, whichpinnedRuntime.tsnever checks for.Steps to reproduce
allow-scriptspolicy in~/.npmrcthat does not covernode-ptyormsgpackr-extract, e.g.:ensurePinnedRuntimeInstalledrunsnpm install --prefix <staging> --no-fund --no-audit t3@<version>.node_modules/node-ptyhas nobuild/and noprebuilds/linux-x64/— theinstallscript was silently skipped (npm warn install-scripts ... blocked because they are not covered by allowScripts).t3code.service. It crash-loops onNodePtyModuleLoadErroreveryRestartSecinterval untilStartLimitBurstis hit and the unit fails permanently. Provider health checks fail every launch. Relay tunnel connects/disconnects in lockstep with the crash loop.Version
0.0.33 (also confirmed still present on
mainas of this report —node-ptyis unchanged inapps/server/package.json, still a plaindependenciesentry with no Linux prebuild vendoring or allow-scripts detection)Environment
Linux x64 (Arch, kernel 7.1.8-arch1-3), Node v26.7.0, npm 12.0.2, systemd user service (
t3code.service)Evidence
Related issues
node-pty, forces a localnode-gypcompile.g++-11package; this report's compile was silently skipped entirely by npm'sallow-scriptspolicy, with a full toolchain present and available the whole time. Different trigger, same missing-prebuild root cause underneath, and neither report's fix (documenting compiler prerequisites) would have caught this one, since no compiler was ever invoked here.Fix applied or workaround
On the affected machine:
This writes an
allowScriptsblock into that runtime's ownpackage.json(scoped to just this pinned runtime, the user's global~/.npmrcpolicy was left untouched), letsnode-gyp rebuildrun for real, and it compiled cleanly with the toolchain already on the machine. Service has been stable since (relay tunnel connected, no PTY errors, no restarts).Suggested upstream fixes:
ensurePinnedRuntimeInstalled(apps/server/src/cloud/pinnedRuntime.ts) could checknpm install-scripts ls(or parse stderr for theblocked because they are not covered by allowScriptswarning) after install and surface it as an actionablePinnedRuntimeInstallError, instead of writing the success sentinel over a silently-broken native module.node-pty(as suggested in bug: npx t3 fails silently on Linux (no Linux prebuilt for node-pty) #2621) so the install script has nothing to skip or compile in the common case.NodePtyAdapter.make's failure currently just crash-loops the whole process (viaEffect.orDie); consider degrading providers that need a PTY instead of taking the whole background service down, so a broken PTY backend doesn't also kill the relay connection.Filed by
Claude (Sonnet 5) via t3 triage