Skip to content

[Bug]: 0.0.41-nightly desktop UI "T3 Code could not load." — t3code:// serves truncated asset (SyntaxError: Unexpected end of input) #11523

Description

@davibarbosa2

Before submitting

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

Area

apps/desktop

Steps to reproduce

  1. Download T3-Code-0.0.41-nightly.20260913.1625-x86_64.AppImage (Linux x86_64), chmod +x it.

  2. Launch it, with the renderer console visible:

    ELECTRON_ENABLE_LOGGING=1 ./T3-Code.AppImage --no-sandbox --enable-logging=stderr
  3. The window opens, the backend becomes ready, then the UI is replaced by "T3 Code could not load."

  4. The renderer console reports a truncated module:

    [INFO:CONSOLE:2] "T3 Code failed to start. SyntaxError: Unexpected end of input", source: t3code://app/assets/index-DRGRqw-K.js (2)
    

Reproduces 100% of launches. I also tried to rule out local causes:

  • Clean Electron profile — launched with --user-data-dir pointed at an empty temp dir: same error (rules out a poisoned HTTP cache).
  • No FUSE / no AppImage — extracted the AppImage with --appimage-extract and ran the extracted t3code binary: same error (rules out squashfs/FUSE truncation).

Expected behavior

The bundled web client loads through t3code://app/... and the desktop reaches the normal UI, as it does on the previous nightly v0.0.41-nightly.20260912.1612.

Actual behavior

The bootstrap module is served truncated over the custom t3code:// protocol, so bootstrap.ts's import("./main").catch(showBootError) fails with SyntaxError: Unexpected end of input and paints the boot-error screen.

The same asset is intact in the package and over the backend HTTP URL, so the truncation is specific to the t3code://app serving path in this build:

  • Inside app.asar, apps/server/dist/client/assets/index-DRGRqw-K.js is present and valid — 7971 bytes, ends with //# sourceMappingURL=index-DRGRqw-K.js.map.
  • GET http://127.0.0.1:3773/assets/index-DRGRqw-K.js returns HTTP 200, content-length: 7971, valid JS (I can load the same URL in Chrome and the app reaches the UI/pairing screen).
  • The renderer, however, loads it via t3code://app/assets/index-DRGRqw-K.js and the module fails to parse.

This is a different symptom from #10719 (0.0.40, does not provide an export named 't' inter-chunk mismatch). The failure here is a parse error, which is what you get from a truncated/mangled body rather than a missing export.

Impact

Blocks work completely

Version or commit

v0.0.41-nightly.20260913.1625 (commit 2db675a)

Environment

  • Ubuntu 24.04.4 LTS, x86_64, Hyprland (Wayland)
  • T3 Code 0.0.41-nightly.20260913.1625, Linux AppImage (T3-Code-0.0.41-nightly.20260913.1625-x86_64.AppImage, 188486467 bytes)
  • Electron 44.1.0 / Chrome 152.0.7977.65
  • Also reproduced with the AppImage extracted to disk and with a fresh --user-data-dir
  • Previous nightly v0.0.41-nightly.20260912.1612 works on the same machine, same profile, same launch flags

Logs or stack traces

ELECTRON_ENABLE_LOGGING=1 ./T3-Code.AppImage --no-sandbox --enable-logging=stderr

Desktop log (backend comes up fine, window is created, then the renderer boot fails):

INFO (#3): runtime logging configured
INFO (#3): using update channel
INFO (#167): backend ready
INFO (#167): main window created
[INFO:CONSOLE:2] "T3 Code failed to start. SyntaxError: Unexpected end of input", source: t3code://app/assets/index-DRGRqw-K.js (2)
[INFO:CONSOLE:2] "T3 Code failed to start. SyntaxError: Failed to execute 'appendChild' on 'Node': Unexpected end of input", source: t3code://app/assets/index-DRGRqw-K.js (2)

Asset status compared across the two paths (broken build):

$ unzip-less read of app.asar    -> apps/server/dist/client/assets/index-DRGRqw-K.js  size 7971  (valid)
$ curl http://127.0.0.1:3773/assets/index-DRGRqw-K.js
  HTTP 200  content-length: 7971  content-type: text/javascript  (valid)
$ renderer via t3code://app/assets/index-DRGRqw-K.js
  SyntaxError: Unexpected end of input

Screenshots, recordings, or supporting files

Screenshot of the boot-error screen attached in the report ("T3 Code could not load." with the Reload button). Happy to provide the full --enable-logging=stderr capture if useful.

Workaround

Downgrade to the previous nightly, which launches normally:

  • v0.0.41-nightly.20260912.1612 → T3-Code-0.0.41-nightly.20260912.1612-x86_64.AppImage
    https://github.com/pingdotgg/t3code/releases/download/v0.0.41-nightly.20260912.1612/T3-Code-0.0.41-nightly.20260912.1612-x86_64.AppImage

Related: #10719 (0.0.40 desktop boot failure — same t3code://app serving path, different error).

Activity

  1. juliusmarminge commented on Sep 13, 2026

    @juliusmarminge
    Member

    Triage

    Accepted as a critical desktop launch bug. v0.0.41-nightly.20260913.1625 (commit 2db675aef) Linux AppImage opens a window, the backend becomes ready, then the renderer dies and paints “T3 Code could not load.”

    This is not a duplicate of #10719. That issue is a parsed ESM chunk missing export { t }. This one is a parse error (SyntaxError: Unexpected end of input) on the t3code://app path only. It is also not #10517 (blank window because the backend never serves the page).

    T3 Code failed to start. SyntaxError: Unexpected end of input
    source: t3code://app/assets/index-DRGRqw-K.js (2)
    

    The same machine, profile, and launch flags work on v0.0.41-nightly.20260912.1612. Clean --user-data-dir and --appimage-extract both still fail, so this is not a poisoned HTTP cache or squashfs/FUSE truncation.

    What the code does

    Production desktop loads t3code://app/ (apps/desktop/src/window/DesktopWindow.ts → getDesktopUrl). apps/desktop/src/electron/ElectronProtocol.ts registers that scheme (stream: true) and protocol.handles every renderer request by Electron.net.fetching the local backend, then re-wrapping the body to attach CSP:

    return new Response(response.body, { status, statusText, headers });

    The backend serves apps/server/dist/client with HttpServerResponse.stream in apps/server/src/http.ts, behind global HttpMiddleware.compression() (gzip/br, min 1024 bytes).

    Startup is:

    1. Packaged index.html loads the Vite entry (src/bootstrap.ts)
    2. bootstrap.ts does import("./main").catch(showBootError)
    3. apps/web/src/lib/bootError.ts logs T3 Code failed to start. and paints “T3 Code could not load.” (the SyntaxError is DEV-only in the UI, which is why it only appears under --enable-logging)

    The log source: …/index-DRGRqw-K.js (2) is the console.error inside showBootError. That entry module ran. The SyntaxError is from whatever import("./main") fetched — typically the main hashed chunk or another /assets/*.js — not proof that index-DRGRqw-K.js itself was truncated. Chromium often attributes a failed import() to the importer.

    That matches the reporter’s checks: index-DRGRqw-K.js is valid in app.asar (7971 bytes) and over http://127.0.0.1:3773/assets/… (Chrome can load that URL and reach pairing). The HTTP graph is good; the custom-protocol hop is not.

    stream: true plus re-wrapping a net.fetch stream while copying Content-Length / Content-Encoding is a known Electron custom-protocol footgun (truncated body, or gzip/length mismatch after Chromium decodes). #9023 enabled streaming for media; it applies to JS modules too.

    Why this nightly?

    ElectronProtocol.ts and static http.ts are unchanged between 20260912.1612 and 20260913.1625. Electron is still 44.1.0. Desktop commits in the window (#7599 quit timeout, #7110 preview UA, paste-as-text from #11442) are not on the renderer serve path. The web client did change substantially (composer attachments, subagent rows, a11y headings, diffs), so chunk sizes/graphs changed on top of the existing stream proxy. A latent protocol-stream bug becoming 100% on a larger or differently chunked bundle is the best fit.

    Related

    No open PR for this truncation.

    Next step

    1. On the failing AppImage, compare all /assets/*.js (especially the target of index-*.js’s import()) over t3code://app vs HTTP vs app.asar — byte length and a tail snippet, not only index-DRGRqw-K.js.
    2. Fix the protocol handler: buffer JS/CSS/HTML (arrayBuffer()), drop stale Content-Encoding, set Content-Length to the decoded size; keep streaming for audio/video only. Optionally revisit feat(desktop): serve packaged renderer locally #8101 (serve packaged files from the asar path).
    3. Check macOS and Windows v0.0.41-nightly.20260913.1625 — same t3code:// path.

    Workaround: v0.0.41-nightly.20260912.1612.

  2. added
    bugSomething is broken or behaving incorrectly.
    acceptedfeature request accepted
    via-triageFiled through npx t3 triage
    on Sep 13, 2026
  3. davibarbosa2 commented on Sep 13, 2026

    @davibarbosa2
    Author

    Measured this from the affected build using the renderer's own DevTools (Electron --remote-debugging-port). The source: in the error is misleading: assets/index-DRGRqw-K.js is not the broken file. showBootError lives in that chunk, so the caught SyntaxError is attributed there. The truncated files are the large chunks.

    Fetching each /assets/*.js from the renderer and comparing byteLength to the entry size in app.asar:

    asset received over t3code:// expected
    assets/main-CFHzQs_8.js 246112 704945
    assets/utils-DmcWaRy7.js 260917 755787
    assets/server-CSx755AA.js 0 19224

    Small assets are intact — assets/index-DRGRqw-K.js is 7971/7971 and its sha256 matches the copy inside app.asar:

    51cfaf46373be9425c39b51bb878ed9aa7e1757dd8367e9067ff8246aba26c99
    

    The same backend serves the full files over its HTTP listener, so the truncation is specific to the t3code:// path and only hits larger bodies:

    $ curl -s -o /dev/null -w '%{size_download}' http://127.0.0.1:3773/assets/main-CFHzQs_8.js
    704945

    Same measurement against the previous nightly (v0.0.41-nightly.20260912.1612), same machine, same flags, same profile setup: 256 assets loaded through t3code://, 0 mismatches, no boot error. The working build even ships larger chunks (utils 755380, main 700010, heic-to ~3 MB), so it is not a simple size limit either.

    Both builds negotiate the same encodings (br/gzip, no zstd), so it does not appear to be zstd:

    # broken build, /assets/utils-DmcWaRy7.js
    req=identity -> content-length: 755787
    req=br       -> content-encoding: br
    req=zstd     -> content-length: 755787   # not compressed
    req=gzip     -> content-encoding: gzip

    So: regression between 20260912.1612 and 20260913.1625 in the delivery of large assets over t3code://, even though nothing landed in apps/desktop/src/electron/ElectronProtocol.ts or apps/server/src/http.ts in that window.

    Minimal way to reproduce the measurement (app launched with --remote-debugging-port=9333), run in the page context via CDP Runtime.evaluate:

    const r = await fetch("t3code://app/assets/main-CFHzQs_8.js");
    (await r.arrayBuffer()).byteLength; // 246112 here, 704945 expected

    Workaround still: use v0.0.41-nightly.20260912.1612.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    acceptedfeature request acceptedbugSomething is broken or behaving incorrectly.via-triageFiled through npx t3 triage

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions