Repository navigation
[Bug]: Delegated worktree setup fails with ENAMETOOLONG on eCryptfs #21637
Description
Before submitting
- I searched existing issues and did not find a duplicate.
- I included enough detail to reproduce or investigate the problem.
Area
apps/server
What happened
A T3-owned delegated task using isolation: "worktree" fails before its project setup command starts. The UI reports:
Workspace preparation failed during run setup script:
Project setup script operation 'openTerminal' failed for thread '<delegated-thread-id>' in '<worktree>'.
The setup output box is empty because terminal history lookup fails before the shell is opened.
Diagnosis
The terminal manager base64url-encodes the complete thread and terminal IDs into one filename. A delegated thread ID in this case is 129 bytes. With terminal ID setup-setup-worktree-2, its history filename is 216 bytes.
The server's home directory is on eCryptfs, which reports PC_NAME_MAX=143. The original server trace shows this error chain:
ProjectSetupScriptOperationError: operation 'openTerminal'
caused by TerminalHistoryError: operation 'read'
caused by FileSystem.access (<terminal-logs>/<216-byte-filename>.log)
caused by ENAMETOOLONG: name too long
Source at the affected release:
Steps to reproduce
- Run T3 Code with its home on eCryptfs. Confirm the filename limit with
getconf NAME_MAX "$HOME"; this host returns143. - Use a project with a synchronous
runOnWorktreeCreate: truesetup script. - Launch a T3-owned delegated task in an isolated worktree with a descriptive client request ID, such as
example-r4-workspace-prepare-engine3. - Preparation reaches "Run setup script", fails at
openTerminal, and never starts the agent.
This standalone filesystem reproduction uses synthetic IDs with the same lengths. Run it with node on a host whose home has the 143-byte limit:
const fs = require("node:fs");
const os = require("node:os");
const path = require("node:path");
const threadId =
"thread:delegated-task:command%3Amcp%3A00000000-0000-4000-8000-000000000000%3Adelegate-task%3Aexample-r4-workspace-prepare-engine3";
const terminalId = "setup-setup-worktree-2";
const encode = value => Buffer.from(value, "utf8").toString("base64url");
const filename = `terminal_${encode(threadId)}_${encode(terminalId)}.log`;
const directory = fs.mkdtempSync(path.join(os.homedir(), ".t3-terminal-repro-"));
try {
console.log("filename bytes:", Buffer.byteLength(filename)); // 216
fs.writeFileSync(path.join(directory, filename), "repro");
} catch (error) {
console.log(error.code); // ENAMETOOLONG on eCryptfs
} finally {
fs.rmSync(directory, { recursive: true, force: true });
}Expected behavior
Valid delegated thread IDs must not prevent setup terminals from opening. History filenames should remain within the filesystem's component limit while preserving distinct thread/terminal identities.
Actual behavior
Terminal history lookup fails with ENAMETOOLONG, aborting workspace preparation before the setup command or agent runs. The UI omits that underlying cause.
Impact
Major degradation or frequent failure. Affected delegated worktree launches cannot start.
Version or commit
0.1.1-nightly.20261018.3335, release source b72740852ffb5ab727e1ae7c042d9e89533bc341. Observed on 19 October 2026.
Environment
Linux x64, kernel 7.0.0-38-generic; headless T3 server; home and T3 state on eCryptfs; Codex provider. The failing operation is server-side filesystem access before provider startup.
Fix applied or workaround
A local, unpublished repair bounds long thread and terminal components using SHA-256, retains short existing filenames, migrates previous histories, and deletes both naming formats. Its maximum combined filename is 133 bytes.
Validation: the new regression fails against the original source; all 98 terminal-manager tests pass with temporary files on the encrypted filesystem; a real setup-shell smoke test exits 0 and persists history there; lint, server typecheck, and server build pass. The rebuilt server also starts against isolated state and answers its environment endpoint. The repair has not been activated on the live server or submitted upstream.
A migration needs care: blindly probing an oversized old filename can itself raise ENAMETOOLONG.
Related observations
After the allocated worktree directory was later absent, clicking Retry failed with TerminalCwdNotFoundError while the UI still displayed the generic openTerminal failure. Directory absence and the retry error were verified; the reason the directory was removed was not established. This is a separate recovery condition, not the cause of the original filename failure.
Related worktree reports #15541 and #21635 concern provisioning failures with leftover worktrees. This report concerns terminal-history access after allocation.
Filed by
Codex (gpt-6.1-sol), at the user's request. Private project names, original task IDs, home paths, and screenshots have been omitted.
Activity
juliusmarminge commentedon Oct 19, 2026
Note
Grok responding on behalf of Julius.
Confirmed on main, so this is accepted as a bug. Terminal history filenames have no length bound. The name is
terminal_plus the base64url thread id, plus the base64url terminal id for non-default terminals (Manager.ts:1206-1212 and 1551-1557). Delegated thread ids are generated by T3 asthread:delegated-task:<command id>, with each part URI-encoded (IdAllocator.ts:191-194 and 261). So a long client request id makes a long filename.readHistorychecks that path before the terminal opens, and a filesystem error there becomes aTerminalHistoryError(Manager.ts:1788-1799).Open #18669 hashes only names that don't fit in 255 bytes. Your 216-byte name would stay as it is, so that change alone wouldn't cover eCryptfs's 143-byte limit. With the id shape in your report, even an empty
clientRequestIdstill gives a 168-byte name for that setup terminal, so a shorter id is not a workaround.