pingdotgg / pingdotgg/t3code

[Bug]: generated macOS LaunchAgent can hit EMFILE under the default maxfiles limit

Open
#11,055 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
TypeScript
Stars
23k
Forks
5.9k
Avg merge
11h 14m
Merged PRs (30d)
357

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

Steps to reproduce
  1. On macOS, check the default per-process open-file limit available to a user LaunchAgent:

    launchctl limit maxfiles
    

    On my host the soft limit is 256:

    maxfiles    256    unlimited
    
  2. Install or update the T3 background service:

    npx t3@nightly service update
    
  3. Confirm that the generated plist has no per-job open-file override:

    plutil -extract SoftResourceLimits.NumberOfFiles raw \
      "$HOME/Library/LaunchAgents/com.t3tools.t3code.service.plist"
    

    This fails because SoftResourceLimits.NumberOfFiles is absent.

  4. Start T3 through the LaunchAgent and use it with the normal T3 data directory and project set. On this host, startup hit EMFILE in the T3 user-data watcher on two consecutive nightly versions.

This last step depends on the number of descriptors/watchers needed by a particular T3 installation, so it may not fail on every fresh profile. The service-definition check is deterministic; the runtime failure was observed twice, on 0.0.36-nightly.20260827.1206 and 0.0.36-nightly.20260828.1208.

Expected behavior

The macOS background service should be able to start its required file watchers under the resource limits established by its generated LaunchAgent. If the required resources cannot be obtained, the service should fail in a clear and actionable way rather than losing a watcher during startup.

Actual behavior

The generated LaunchAgent inherits the host's soft maxfiles limit of 256. T3 then logged EMFILE: too many open files, watch while creating a FileSystem.watch on its user-data directory.

The server continued startup afterward in the captured runs, but the watcher failure makes the resulting state unclear and can leave settings or user-data changes unwatched.

Running service update recreates the plist without a locally added resource limit, so the exposure returns after an otherwise normal service repair/update.

Impact

Minor bug or occasional failure

Version or commit

Runtime failure observed in 0.0.36-nightly.20260827.1206 and 0.0.36-nightly.20260828.1208. The current plist renderer still emits no SoftResourceLimits on main at d29c56a5c404cb0f58d3b2ac41762fa0d0ac28d4.

Environment

macOS 26.6.2 (Apple Silicon), launchd user agent, Homebrew Node; current host soft maxfiles limit: 256

Logs or stack traces
PlatformError: Unknown: FileSystem.watch (<HOME>/.t3/userdata)
    at systemError (file://<HOME>/.t3/runtime/versions/0.0.36-nightly.20260827.1206/node_modules/t3/dist/Path-DtM9tupg.mjs:142:34)
    at FSWatcher.<anonymous> (file://<HOME>/.t3/runtime/versions/0.0.36-nightly.20260827.1206/node_modules/t3/dist/NodeServices-BkTDLo1l.mjs:1546:33)
    at FSWatcher.emit (node:events:514:20)
    at FSWatcher._handle.onchange (node:internal/fs/watchers:275:12)
    at server.startup.settings.start (file://<HOME>/.t3/runtime/versions/0.0.36-nightly.20260827.1206/node_modules/t3/dist/bin.mjs:67632:99) {
  [cause]: Error: EMFILE: too many open files, watch
      at FSWatcher._handle.onchange (node:internal/fs/watchers:269:21)
}

The same failure appeared again with 0.0.36-nightly.20260828.1208 at the same FileSystem.watch boundary.

Investigation notes

I inspected the generated plist, the loaded launchd environment, the historical boot-service log, and the current renderer.

I cannot tell from outside the project whether the preferred approach is to change the LaunchAgent resources, reduce descriptor pressure, change watcher behavior, or surface a clearer startup failure. A locally higher per-job limit stopped the errors in my observations, but that correlation is not intended as a prescribed upstream value or implementation.

Workaround

I currently maintain a local per-job open-file limit in the LaunchAgent and verify it after service updates. The exact value is local policy, not a proposed T3 default.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with apps/server/src/cloud/bootService.ts around lines 102-157 and inspect how the macOS LaunchAgent plist is rendered. Reproduce the generated plist with npx t3@nightly service update, check launchctl limit maxfiles, and trace the startup watcher failure described in the issue. Done means the service handles the host file-descriptor limit reliably and startup behavior is clear, with coverage for the generated service definition or failure path.

Written by the indexing model from the issue text.

Assessment

Tech stack
macos, node.js, typescript
Domain
backend, infrastructure
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Active
Clarity
Mostly clear
Newbie friendliness
52/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.