fix(server): close mkstemp fd in _tokenize_prompt to prevent fd leak#15
Merged
Merged
Conversation
`tempfile.mkstemp()` returns `(fd, path)`. The previous code kept only the path (index [1]) and discarded `fd`. Python's GC does not close integer file descriptors, so the fd stays open in the OS until the process dies. Every chat request leaks exactly one fd. Under sustained load, the process hits `RLIMIT_NOFILE` (default 1024 on a systemd service without explicit `LimitNOFILE=`). Once that wall is hit, `socket.accept()` starts throwing `OSError: [Errno 24] Too many open files` and the API stops accepting new HTTP connections. Reproduction in our deployment: Lucebox behind ~6 concurrent client services (4 Hermes agents + a Hindsight memory server + a Honcho peer-model service + a Firecrawl extractor). Within roughly an hour of normal aux traffic, FD count reached 1023/1024 and new requests were rejected even though the model itself was healthy. Fix: use `os.fdopen(fd, "wb")` which takes ownership of `fd` and closes it when the `with` block exits. The on-disk temp file is still cleaned up by the existing `prompt_bin.unlink()` in `chat_completions` (both streaming and non-streaming paths), so there is no change to disk behavior. Verified empirically: FD count delta across three probe chat requests went from +3 (one leak per request) to 0 after the patch.
oliveagle
pushed a commit
to oliveagle/lucebox-hub
that referenced
this pull request
May 22, 2026
fix(server): close mkstemp fd in _tokenize_prompt to prevent fd leak
easel
added a commit
to easel/lucebox-hub
that referenced
this pull request
May 29, 2026
- lucebox/README.md: fix the relative link to `cli.py`; resolves to `src/lucebox/cli.py` (the actual location), not the nonexistent `lucebox/cli.py` (Luce-Org#15). - luce-bench/NOTICE: the bundled forge_eval LICENSE says "Copyright (c) 2025-2026 Antoine Zambelli", not 2024 — sync NOTICE with the actual upstream LICENSE (Luce-Org#16). - luce-bench/src/lucebench/areas/__init__.py: `__all__` was missing agent / agent_recorded / forge / longctx / smoke. Add the imports + list entries so `from lucebench.areas import *` matches the actual area surface (Luce-Org#17). Addresses cubic Luce-Org#15–Luce-Org#17 (P3) on PR Luce-Org#285.
davide221
added a commit
that referenced
this pull request
Jun 7, 2026
The origin/main merge resolved the server/deps/llama.cpp gitlink to the PR's older b896cf69 (llama #12 hip-shim), rewinding it from main's 570d9785 and dropping llama #13 (crash), #14 (pflash-sparse stream-sync / BSA fix) and #15 (fattn-sparse hip cudaStreamDefault). Point the gitlink back to 570d9785 (a strict superset — b896cf69 is its ancestor) so the PR no longer downgrades llama.cpp / drops the BSA fix. Rebuilt + smoke-tested against 570d9785: sg_b100 96/96 bit-exact vs all-GPU, offload 60% 99.3 tok/s, all-GPU 119.5. Co-Authored-By: WOZCODE <contact@withwoz.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The bug
In
dflash/scripts/server.py,_tokenize_prompt()calls:tempfile.mkstemp()returns(fd, path). This code keeps only the path (index[1]) and discardsfd. Python's GC does not close integer file descriptors, so the fd stays open in the OS until the process dies. Every chat request leaks exactly one fd.Under sustained load the process hits
RLIMIT_NOFILE(default 1024 on a systemd service without explicitLimitNOFILE=). Once the wall is reached,socket.accept()starts throwingOSError: [Errno 24] Too many open filesand the API stops accepting new HTTP connections — even though the model process itself is still healthy.Reproduction
In our deployment we have Lucebox sitting behind ~6 concurrent client services (4 Hermes agents + a Hindsight memory server + a Honcho peer-model service + a Firecrawl extractor, all OpenAI-compatible). Within roughly an hour of normal aux traffic, FD count reached 1023/1024 and new requests were rejected.
/proc/<lucebox-pid>/fdshowed the composition: only ~5 actual sockets and pipes, and ~1000 orphan/tmp/tmp*.binfile descriptors — exactly the pattern of a leakedmkstempfd.Journal log excerpt:
The fix
Use
os.fdopen(fd, "wb")which takes ownership offdand closes it when thewithblock exits:The on-disk temp file is still cleaned up by the existing
prompt_bin.unlink()inchat_completions(both streaming and non-streaming paths), so there is no change to disk behaviour — only the fd lifecycle changes.Verification
Also raised
LimitNOFILEfrom the default 1024 to 65536 on our systemd unit as a defence-in-depth measure (unrelated to this PR). The code change alone is the correct fix; the limit bump is independent and up to the operator.