[Bug]: linux-arm64 CLI from nightly 20261022.3512 cannot exec on 16 KiB-page kernels (Raspberry Pi 5)#23433

opened7d agobyCchristhomas

Before submitting

  • I searched existing issues and did not find a duplicate.
  • I included enough detail to reproduce or investigate the problem.

Area

Build, CI, or release tooling

Steps to reproduce

On a linux-arm64 host whose kernel uses 16 KiB pages (getconf PAGESIZE prints 16384). This includes a Raspberry Pi 5 on its default rpi-2712 kernel.

  1. npx t3@0.1.2-nightly.20261022.3512 --version
  2. Or run the extracted binary directly: ~/.t3/runtime/versions/0.1.2-nightly.20261022.3512/t3 --version

Expected behavior

It prints t3 v0.1.2-nightly.20261022.3512, as 0.1.2-nightly.20261021.3499 does on the same machine.

Actual behavior

The kernel refuses to load the binary. The shell shows Segmentation fault and exit code 139:

1$ strace -f ~/.t3/runtime/versions/0.1.2-nightly.20261022.3512/t3 --version
2execve(".../0.1.2-nightly.20261022.3512/t3", [..., "--version"], ...) = -1 EINVAL (Invalid argument)
3+++ killed by SIGSEGV +++

Through npx it prints nothing at all. The npm launcher (bin/t3.mjs) turns the child's SIGSEGV into process.exit(139) without a message, so the command just seems to do nothing.

Cause

The two newest LOAD segments in the 3512 binary are aligned to 4 KiB (0x1000). A kernel with 16 KiB pages cannot map them. In 3499, every LOAD segment is aligned to 64 KiB.

readelf -lW t3, 3499 (works):

1 LOAD 0x000000 0x0000000000400000 ... 0x409510 0x409510 R 0x10000
2 LOAD 0x410000 0x0000000000810000 ... 0x2978c90 0x2978c90 R E 0x10000
3 LOAD 0x2d90000 0x0000000003190000 ... 0x3dfd208 0x3dfd208 R 0x10000
4 LOAD 0x6b8d208 0x0000000006f8d208 ... 0x06dca8 0x0bedf8 RW 0x10000
5 LOAD 0x8dde000 0x0000000010dde000 ... 0xffe000 0xffe000 R 0x10000 <- .note.node.sea

3512 (fails):

1 PHDR 0x6dad000 0x00000000071ad000 ... 0x0002a0 0x0002a0 R 0x8
2 LOAD 0x000000 0x0000000000400000 ... 0x41d390 0x41d390 R 0x10000
3 LOAD 0x420000 0x0000000000820000 ... 0x29d82f0 0x29d82f0 R E 0x10000
4 LOAD 0x2e00000 0x0000000003200000 ... 0x3eda298 0x3eda298 R 0x10000
5 LOAD 0x6cdee00 0x00000000070eee00 ... 0x0bdb00 0x0bdb00 RW 0x10000
6 LOAD 0x6dad000 0x00000000071ad000 ... 0x000508 0x000508 R 0x1000 <- new segment holding the moved program headers
7 LOAD 0x8faf000 0x000000001015d000 ... 0x1003000 0x1003000 R 0x1000 <- .note.node.sea, was 0x10000

The only build change between the two nightlies is #23165 (ed9b7ca9c5), which moved the single-executable from Node 26.8.2 to 26.12.1. The binaries confirm this: 3499 embeds v26.8.2, 3512 embeds v26.12.1. So the --build-sea injector in Node 26.12.1 now writes 4 KiB-aligned segments on linux-arm64. 26.8.2 did not.

The arm64 smoke test can't catch this, because GitHub's arm64 runners use 4 KiB pages. A build-time check would catch it: fail the linux-arm64 archive if any LOAD in readelf -lW has an alignment below 0x10000.

Impact

Blocks work completely

Version or commit

0.1.2-nightly.20261022.3512 (3b95e7831b11). Last working: 0.1.2-nightly.20261021.3499 (b5a226e49c52).

Environment

Raspberry Pi 5 Model B Rev 1.1, Debian 13 (trixie), kernel 6.18.50+rpt-rpi-2712 (16 KiB pages), node v22.23.2 for npx

Logs or stack traces

See above. dmesg shows nothing; the failure is at execve.

Synced from GitHub

Same crash on Apple Silicon under Asahi Linux, which also runs a 16 KiB-page kernel. It now affects the preview channel too, not only nightly, and it is still present in the newest nightly.

Environment

  • MacBook Pro (Apple Silicon), Fedora Linux Asahi Remix 44, kernel 7.1.13-402.asahi.fc44.aarch64+16k
  • getconf PAGESIZE → 16384; glibc 2.43
  • Install: npm global (npm install -g t3@<version>), run as a t3 serve user service

Builds tested (t3 --version)

BuildResult
0.1.1 (latest)OK
0.1.2-nightly.20261021.3499OK
0.1.2-preview.20261022.3516 (preview)SIGSEGV, exit 139
0.1.2-nightly.20261022.3541 (release archive, checksum verified against SHA256SUMS)SIGSEGV, exit 139

readelf -lW t3, preview 3516 (3541 is identical apart from the size of the last segment):

1LOAD 0x000000 0x0000000000400000 ... R 0x10000
2LOAD 0x420000 0x0000000000820000 ... R E 0x10000
3LOAD 0x2e00000 0x0000000003200000 ... R 0x10000
4LOAD 0x6cdee00 0x00000000070eee00 ... RW 0x10000
5LOAD 0x6dad000 0x00000000071ad000 ... R 0x1000
6LOAD 0x8faf000 0x000000001015d000 ... R 0x1000

Working 3499, all segments 64 KiB-aligned:

1LOAD 0x000000 0x0000000000400000 ... R 0x10000
2LOAD 0x410000 0x0000000000810000 ... R E 0x10000
3LOAD 0x2d90000 0x0000000003190000 ... R 0x10000
4LOAD 0x6b8d208 0x0000000006f8d208 ... RW 0x10000
5LOAD 0x8dde000 0x0000000010dde000 ... R 0x10000

This matches the layout in this issue and in #23477 / #23773. Because the defect has reached preview, a promotion to latest would break every Linux arm64 host with 16 KiB pages (Asahi on Apple Silicon, Raspberry Pi 5 default kernel). We have pinned latest 0.1.1 until a fixed build ships, and can test a candidate build on Asahi.

— drafted by David Helmus's agent

Synced from GitHub

This now affects the stable channel too. The v0.1.2 release (Latest) has the same problem on a Raspberry Pi 5.

  • Kernel 6.18.50+rpt-rpi-2712, getconf PAGESIZE prints 16384
  • t3-0.1.2-linux-arm64.tar.gz (sha256 matches SHA256SUMS): ./t3 --version exits 139 with Segmentation fault, every time
  • 0.1.1 works on the same machine

t3 update --yes from 0.1.1 fails in the staged binary check (PinnedRuntimeInstallError ... Process interrupted due to receipt of signal: 'SIGSEGV'), so the background service stays on 0.1.1. The update aborts cleanly, but stable Pi 5 users are stuck on 0.1.1 until this is fixed.

Synced from GitHub

Local workaround for 16 KiB-page hosts until #23443 ships. Tested on Apple Silicon running Arch Linux ARM with the Asahi kernel (7.2.12-1-1-ARCH, getconf PAGESIZE = 16384, glibc 2.43), with 0.1.2-nightly.20261022.3541.

The crash comes from the two LOAD segments that LIEF adds during SEA injection:

  • The relocated PHDR segment at vaddr 0x71ad000 shares a 16 KiB page with the end of .bss, which ends at 0x71ac900. The kernel maps it read-only over that page, and the first write to the tail of .bss faults.
  • The .note.node.sea segment has file offset 0x8faf000 and vaddr 0x1015d000, and the two aren't congruent mod 16 KiB.

The script below rewrites only virtual addresses in the headers; file contents stay where they are. It gives each 4 KiB-aligned LOAD segment a vaddr congruent to its file offset mod 64 KiB, placed past the previous segment's last 64 KiB page, and sets its alignment to 0x10000. PT_PHDR, PT_NOTE and section addresses inside a moved segment move with it. Node finds the SEA blob by walking PT_NOTE headers, so it still finds it. The moved PHDR segment no longer sits at first LOAD vaddr + e_phoff, so kernels older than 5.18 (which compute AT_PHDR that way) won't load the result. I've only tested on 7.2.

After patching, 3541 gives:

1$ ./t3 --version
2t3 v0.1.2-nightly.20261022.3541
3$ ./t3 __service-preflight --database-path ~/.t3/userdata/statev2.sqlite --launcher-protocol 3
4{"status":"ready","version":"0.1.2-nightly.20261022.3541","launcherProtocol":3}

The other native files in the archive (resource-monitor, node-pty, keyring, ffi-rs, fff, the Cursor SDK binaries) are already 64 KiB-aligned and needed no changes.

To get a t3 service install onto the new version, pre-stage the patched build. The updater skips the download when ~/.t3/runtime/versions/<version>/t3 exists with a matching .install-complete, and then runs the preflight against the staged binary:

sh
1V=0.1.2-nightly.20261022.3541
2mkdir t3-$V && cd t3-$V
3curl -fsSLO https://github.com/pingdotgg/t3code/releases/download/v$V/SHA256SUMS
4curl -fsSLO https://github.com/pingdotgg/t3code/releases/download/v$V/t3-$V-linux-arm64.tar.gz
5grep linux-arm64 SHA256SUMS | sha256sum -c
6mkdir x && tar -xf t3-$V-linux-arm64.tar.gz -C x --strip-components=1
7python3 -I /path/to/realign-elf-16k.py x/t3
8printf '%s\n' $V > x/.install-complete
9chmod 700 x && mv -T x ~/.t3/runtime/versions/$V

Then run the update from the app. My service went from 0.1.1-nightly.20261020.3411 to 3541, and the launcher recorded it as committed.

realign-elf-16k.py
python
1"""Move 4K-aligned LOAD segments of an aarch64 ELF so they load on 16K/64K-page kernels.
2
3Only virtual addresses change; file contents stay put. Each 4K-aligned LOAD gets a new
4vaddr congruent to its file offset mod 64K, placed past the end of the preceding segment's
564K page. PT_PHDR, PT_NOTE and section addresses inside a moved segment move with it.
6"""
7import struct, sys
8
9ALIGN = 0x10000
10path = sys.argv[1]
11data = bytearray(open(path, "rb").read())
12assert data[:4] == b"\x7fELF" and data[4] == 2 and data[5] == 1
13e_phoff, e_shoff = struct.unpack_from("<QQ", data, 0x20)
14e_phentsize, e_phnum, e_shentsize, e_shnum = struct.unpack_from("<HHHH", data, 0x36)
15
16PH = "<IIQQQQQQ" # type flags offset vaddr paddr filesz memsz align
17phdrs = [list(struct.unpack_from(PH, data, e_phoff + i * e_phentsize)) for i in range(e_phnum)]
18loads = sorted((p for p in phdrs if p[0] == 1), key=lambda p: p[3])
19
20moves = [] # (old_start, old_end, delta)
21prev_end = 0
22for p in loads:
23 off, vaddr, memsz, align = p[2], p[3], p[6], p[7]
24 if align >= ALIGN and (vaddr - off) % ALIGN == 0 and vaddr >= prev_end:
25 prev_end = -(-(vaddr + memsz) // ALIGN) * ALIGN
26 continue
27 base = -(-prev_end // ALIGN) * ALIGN
28 new = base + (off % ALIGN)
29 while new < prev_end:
30 new += ALIGN
31 moves.append((vaddr, vaddr + memsz, new - vaddr))
32 prev_end = -(-(new + memsz) // ALIGN) * ALIGN
33
34def shift(addr):
35 for lo, hi, d in moves:
36 if lo <= addr < hi:
37 return addr + d, d
38 return addr, 0
39
40for i, p in enumerate(phdrs):
41 if p[0] in (1, 4, 6): # LOAD, NOTE, PHDR
42 new, d = shift(p[3])
43 if d:
44 print(f"phdr[{i}] type={p[0]} vaddr {p[3]:#x} -> {new:#x}")
45 p[3] = p[4] = new
46 if p[0] == 1:
47 p[7] = ALIGN
48 struct.pack_into(PH, data, e_phoff + i * e_phentsize, *p)
49
50for i in range(e_shnum):
51 o = e_shoff + i * e_shentsize
52 addr = struct.unpack_from("<Q", data, o + 0x10)[0]
53 if addr:
54 new, d = shift(addr)
55 if d:
56 struct.pack_into("<Q", data, o + 0x10, new)
57 print(f"shdr[{i}] addr {addr:#x} -> {new:#x}")
58
59open(path, "wb").write(data)

This is a stopgap; the real fix is aligning these segments at build time, as #23443 does.

Synced from GitHub

Shouldn't a pipeline test have picked this up? I think this also needs to be added here cause it sounds like a very mechanical fault. Something quite easy to check

Synced from GitHub

Another local workaround, for hosts stuck on stable v0.1.2: instead of rewriting ELF vaddrs, run the JS bundle that the SEA note already contains, using the system Node.

readelf -SW t3 shows a .note.node.sea section (note header + build path + the bundle, which starts at #!/usr/bin/env node). Extract it and run it:

sh
1python3 - "$PWD/t3" "$PWD/bin.mjs" <<'PY'
2import struct, sys
3src, dst = sys.argv[1:3]
4b = open(src, "rb").read()
5shoff, = struct.unpack_from("<Q", b, 0x28)
6entsz, shnum, shstr = struct.unpack_from("<HHH", b, 0x3A)
7sh = lambda i: struct.unpack_from("<IIQQQQIIQQ", b, shoff + i * entsz)
8strtab = sh(shstr)[4]
9for i in range(shnum):
10 name, _, _, _, off, _, _ = sh(i)[:7]
11 end = b.index(b"\0", strtab + name)
12 if b[strtab + name:end] == b".note.node.sea":
13 namesz, descsz, _ = struct.unpack_from("<III", b, off)
14 blob = b[off + 12 + (namesz + 3) // 4 * 4:][:descsz]
15 open(dst, "wb").write(blob[blob.index(b"#!/usr/bin/env node"):])
16 break
17PY
18node bin.mjs --version # t3 v0.1.2

A 3-line t3 wrapper that execs node <dir>/bin.mjs "$@" is enough to keep the rest of the CLI working, including t3 service install and t3 connect.

Verified on: Raspberry Pi 5, Kali arm64, kernel 6.12.34+rpt-rpi-2712, getconf PAGESIZE = 16384, glibc 2.42, system Node v24.15.0. Reproduced on v0.1.2 (stable) and 0.1.3-nightly.20261023.3566; both run fine this way.

Trade-off: process.isSea() reports false, so the CLI behaves like the npm-launcher path. In practice only messaging differs.

Also confirming Project516's report: t3 update aborts at verifying the pinned t3 runtime with Process interrupted due to receipt of signal: 'SIGSEGV' and leaves the running version alone, so any 16 KiB-page host needs this post-processing after each update until #23443 lands.

Synced from GitHub

Confirming the stable v0.1.2 release on Asahi Linux (Apple Mac mini M1) as well (so far Asahi was reported on preview/nightly, and stable on Pi 5).

1- Fedora Linux Asahi Remix 44, kernel 7.1.13-402.asahi.fc44.aarch64+16k, getconf PAGESIZE = 16384, glibc 2.43
2- t3-0.1.2-linux-arm64.tar.gz, sha256 df112e1f…abd0f (matches SHA256SUMS)
3- ./t3 --version and ./t3 --help both exit 139. 0.1.1 works on the same machine.
1readelf -lW t3 matches the layout above, with the last two LOAD segments at 0x1000:
2
3LOAD 0x6dad000 0x00000000071ad000 ... 0x000508 0x000508 R 0x1000
4LOAD 0x8faf000 0x000000001015d000 ... 0x1004000 0x1004000 R 0x1000

Running it in a container doesn't help, because containers share the host's kernel and page size. It crashes the same way in debian:bookworm-slim and ubuntu:24.04 under rootless podman. That's probably also what's behind the Docker Sandbox report above.

On a fresh install, install.sh stops at its sanity check, with no hint about page size:

1$ curl -fsSL https://t3.codes/install.sh | T3CODE_VERSION=0.1.2 sh
2t3 install: the downloaded executable does not run

A clearer message would help people find this issue. For example, a check of getconf PAGESIZE plus the segment alignment, or at least a line pointing here when --version dies with SIGSEGV on arm64.

Workaround from above works as temporary fix.

Synced from GitHub

Still present in 0.1.3-nightly.20261024.3617 (Raspberry Pi 5, kernel 6.18.50+rpt-rpi-2712, getconf PAGESIZE = 16384). It has the same two LOAD segments with 0x1000 alignment at offsets 0x6dad000 and 0x8faf000. ./t3 --version exits 139, and the in-app server update fails with "Could not prepare t3@0.1.3-nightly.20261024.3617" because the staged __service-preflight gets SIGSEGV.

For anyone else on a Pi: the vaddr-rewrite workaround above still works on this build. If you put the patched runtime in ~/.t3/runtime/versions/<version>/ with an .install-complete file containing the version, the in-app updater uses it as a cached install. Its preflight passes and the update completes.

Synced from GitHub

Same on a Docker Sandbox (sbx, aarch64 microVM on an Apple Silicon Mac, kernel 7.0.14, getconf PAGESIZE = 16384), as @stefansedich mentioned. Stable v0.1.2 exits 139 on ./t3 --version, v0.1.1 runs fine. The app only shows SshCommandError: Segmentation fault.

A stopgap for the SSH integration until #23443 ships: copy ~/.t3/runtime/versions/0.1.1 to versions/0.1.2 and write 0.1.2 into its .install-complete. The bootstrap then skips the download and the app connects.

🤖 Written assisted by my Claude

Synced from GitHub

Still reproducible in 0.1.3-nightly.20261024.3643 and 0.1.3-nightly.20261025.3655 on Fedora Asahi Linux ARM64 (kernel 7.1.13-402.asahi.fc44.aarch64+16k, getconf PAGESIZE = 16384). Fresh release archives matched their published SHA256SUMS; the unmodified binaries crash on --version, and the in-app server update fails with Server update failed: Could not prepare t3@0.1.3-nightly.20261025.3655 because the staged __service-preflight receives SIGSEGV. The desktop SSH bootstrap independently fails its binary version check. In .3655, readelf -lW still shows the read-only SEA-note LOAD at file offset 0x8faf000, virtual address 0x1015d000, alignment 0x1000; offset and virtual address differ by 0x2000 modulo 16 KiB. On this host, moving that LOAD's virtual/physical addresses and the matching PT_NOTE/section addresses forward by 0x2000 allowed both versions to run. The repaired .3655 passed --version, service preflight, a full startup with a temporary data directory, and the real service update; the running server then returned HTTP 200 with the expected version through an SSH tunnel. This is a local workaround—the next nightly downloads a fresh affected binary. For artifact identification, the .3655 linux-arm64 archive SHA-256 was e90d8f148658be8926f4ab0733a4f240c42188117ae573b4319618edb5e4e0bd.

Reported with assistance from Codex; observations and workaround verified on the affected host.

Synced from GitHub

Confirming the stable v0.1.2 failure on a Raspberry Pi 5, with additional loader evidence and a verified local recovery.

Environment and artifact

  • Linux aarch64, kernel 6.18.50+rpt-rpi-2712; getconf PAGESIZE = 16384.
  • v0.1.1 runs on the same host.
  • Official t3-0.1.2-linux-arm64.tar.gz, checked against the release's published SHA256SUMS: df112e1f19772b4055a28c97b032eb6d038d5d7182015f1cafd3254ad6fabd0f.
  • Original extracted executable SHA-256: 0cd8e0086243cfe79c5160f16508696a3fd02171725a251990529f7c3321a52d.

Reproduction and diagnosis

Extract the official release archive on the affected host, then run ./t3 --version. The unmodified binary exits 139, without needing an existing database.

text
1$ strace ./t3 --version
2execve("./t3", ["./t3", "--version"], ...) = -1 EINVAL (Invalid argument)
3+++ killed by SIGSEGV +++
4
5$ /lib/ld-linux-aarch64.so.1 --list ./t3
6./t3: error while loading shared libraries: ./t3: ELF load command address/offset not page-aligned

The application reports:

text
1Server update failed: Could not prepare t3@0.1.2.
2Pinned runtime install failed while running the staged service preflight.
3Process interrupted due to receipt of signal: 'SIGSEGV'

The SEA-note LOAD has file offset 0x8faf000, virtual address 0x1015d000, and declared alignment 0x1000. The offset and address are respectively 0x3000 and 0x1000 modulo 0x4000: they differ by 8192 bytes modulo the host page size. This establishes a loader failure before the application's entry point.

Local recovery verified

As a narrowly scoped diagnostic workaround, inserting 8192 bytes before that segment and updating all affected file offsets (program headers, section headers, and the section-header table pointer) made this exact binary load. Virtual addresses and payload contents were preserved.

The adjusted binary passed:

  • --version: t3 v0.1.2.
  • __service-preflight --database-path … --launcher-protocol 3: {"status":"ready","version":"0.1.2","launcherProtocol":3}.
  • Full startup with a separate temporary data directory; environment endpoint returned HTTP 200 and server version 0.1.2.
  • The normal CLI update using the staged runtime, after a database backup. The live service returned HTTP 200/version 0.1.2; SQLite quick_check returned ok.

This workaround was tested only on this 16 KiB host. It is not a general ELF repair or evidence of 64 KiB compatibility; in particular it does not address the separate relocated-PHDR/RW-page overlap discussed above.

Proposed upstream fix and acceptance checks

#23443 appears to address the right build boundary: preserve the security-fixed Node runtime while using a compatible Linux ARM64 SEA injection path.

Please retain the final-executable validation at both injection and archive staging:

  1. Check actual PT_LOAD file-offset/virtual-address congruence and page-rounded mapping overlap at 64 KiB, covering the supported 4/16/64 KiB page sizes. Changing or checking p_align alone is insufficient.
  2. Smoke-test the packaged archive on a real 16 KiB ARM64 kernel: --version, service preflight, and full temporary-directory startup with an HTTP health/version check. Add a 64 KiB runtime check when available.
  3. Publish the corrected artifact under a new patch version with new checksums so version-keyed runtime caches do not retain the affected executable.
  4. Surface the staged executable's signal/loader failure in the update error, so users can distinguish this packaging fault from download or database failures.
Synced from GitHub

broken in docker sbx on Mac via docker.io/sbx/t3code-kit:latest . :'(

Synced from GitHub

Also unable to use in a docker sandbox anymore on an M5 mac with either nightly or stable channels.

Connecting via SSH in the desktop app to a previously working sandbox fails with the following diag info:

1This device: ok
2T3 Code on claude-t3dev-sandbox: FAILED
3Cause: remote-unavailable
4Attempts: 1
5Next retry: 2026-10-26T10:05:26.740Z
6Route: Direct
7App version: 0.1.3-nightly.20261026.3717
8Host version: 0.1.1-nightly.20261016.3203
9Error: Could not prepare the SSH environment: Error invoking remote method ‘desktop:ensure-ssh-environment’: SshCommandError: Segmentation fault

Also fails with the following when trying to setup a new docker sandbox from scratch using the t3code kit too:

1❯ sbx run claude --kit docker.io/sbx/t3code-kit:latest
2
3 SANDBOX
4 Name: claude-sandbox-test
5 Agent: claude
6 Kit: docker.io/sbx/t3code-kit:latest
7 Workspace: ~/AI/sandbox-test (rw)
8 Skills: 0 entries · linked, read-only
9 Source: ~/Library/Application Support/com.docker.sandboxes/sandboxes/agent-skills
10 Target: /home/agent/.claude/skills
11 Image: docker/sandbox-templates:claude-code-docker
12 CPU: 10
13 Memory: 16 GiB
14
15 IMAGE
16 ✓ image — ready
17
18 AGENT
19 setting 2 environment variables
20 running 4 install commands
21 ✓ set -e ws="${WORKSPACE_DIR:-/}" esc=$(printf '%s' "$ws" | s… (kit=claude, user=0, 9ms)
22 ✓ set -e mkdir -p /home/agent/.claude HELPER='' if [ "${SBX_C… (kit=claude, user=agent, 13ms)
23 ✓ set -e [ -n "$MCP_GATEWAY_URL" ] || exit 0 export PATH="$HO… (kit=claude, user=agent, 338ms)
24 ✗ set -euo pipefail command -v npm >/dev/null || { echo "npm … (kit=t3code, user=0, exit 1)
25 ── captured output ──
26 added 1 package in 239ms
27 t3 installed but will not start, see the error above. On a base image without apt-get, install libatomic1 with its own package manager first.
28 ──────────────────────
29 registering 3 startup commands, run on every container start
30 ○ sh -c chown -R agent:agent /home/agent/.claude/projects /ho… (kit=claude, user=0)
31 ○ sh -c { command -v apt-get && apt-get update -qq -y || true… (kit=claude, user=root)
32 ○ sh -c set -e [ -n "$MCP_GATEWAY_URL" ] || exit 0 export PAT… (kit=claude, user=agent)
33 view startup log: cat /var/log/sbx-kit-startup.log
34
35 error: cannot create sandbox: failed to apply kit to sandbox
Synced from GitHub

Also affects Raspberry Pi 4: Linux aarch64, kernel 6.12.62+rpt-rpi-2712, getconf PAGESIZE = 16384. Version 0.1.1 works; npx t3@0.1.2 triage returns without output.

Synced from GitHub

Hit this on 0.1.3-nightly.20261026.3717 too (Raspberry Pi 5, 16K pages). The binary patch from above works for a systemd service install, with two extra steps:

  1. Point the unit's ExecStart at the patched version's t3 __service-launcher.
  2. In service-state.json (under .t3/runtime/), set activeVersion and the update record's targetVersion to the new version. Changing only activeVersion makes the launcher exit with Service state is invalid or unsupported.
Synced from GitHub

Still reproduces on 0.1.3-nightly.20261027.3754, on two more 16 KiB-page hosts:

  • Asahi Linux on an M1 Mac mini (getconf PAGESIZE → 16384): 0.1.2, 0.1.3-preview.20261026.3702, 0.1.3-nightly.20261026.3740 and 0.1.3-nightly.20261027.3754 all exit 139 on t3 --version, both through npx t3@<version> and running the extracted runtime binary directly. 0.1.1-nightly.20261016.3218 and older run on the same machine.
  • Raspberry Pi 5 on the default 16 KiB kernel: same page size. Not run there; that host stays on 0.1.1.

Both hosts run t3 serve as a user service, so they can't take any update until this is fixed.

Human Theo here coming in before my agents do.

You guys know this is an UPSTREAM NODE ISSUE right?? Why has everyone made it our problem and NOBODY has reported it to node???

You're making the worst possible case for Linux ARM users here. Your platform IS NOT PROPERLY SUPPORTED BY NODE, which supports EVERYTHING.

I'm annoyed because

  1. This proves your platform is shit
  2. This proves you guys don't understand where the problem lives (again, 0 upstream reports to Node)

That said, I am kind. We will fix this. One time. Ever.

If there is ONE MORE REGRESSION FOR ARM LINUX that exists OUTSIDE OF OUR CODEBASE, I will be killing Arm builds forever, and it is all of your fault.

Good day 🫡

Note

🤖 Claude Opus 5.5 responding on behalf of Theo

Sorry about that, Theo can be rude sometimes, and thank you for the detailed reports here. That said, he is kinda right, and this should have been reported to Node first.

The cause is nodejs/node#65564, which added 4 KiB-aligned segments to --build-sea output that kernels with 16 KiB or 64 KiB pages cannot map. We filed the upstream bug at https://github.com/nodejs/node/issues/67048, so please follow it there and add any useful details.

Next time, please report core-dependency bugs like this upstream first, with a link back here. We are shipping a TEMPORARY workaround in https://github.com/pingdotgg/t3code/pull/25825 as a one-time exception, and we will remove it once Node ships a fix. It will reach users in the next T3 Code release, since installed runtimes are cached by version.

Synced from GitHub

Not my circus, not my monkeys, I reported the issue but then I didn't pay it much time until your message about node. I agree that whislt it's annoying that an upstream dependency that you rely on causes you this issue, Maybe the solution is a bot that can triage issues and present this kind of information without the mostly understandable explosion ;)

I would have thought the best solution here is to have a pipeline job that detects these things which are mechanical and block builds from being produced and report the issues, but compiling a binary that instantly crashes, whilst it's node thats causing it, you are still the one publishing it.

If you publish it, then surely you tested it right? Even mechanical tests would have picked this up and all arm64 builds for raspberry pi would be blocked. It would cause people to investigate why and the problem would appear. But you don't have that, so you're just publishing broken builds.

I think the first step is, make the pipeline break when the build is broken, and have an automated process that publishes the reason why. Then at least you have this covered?