Skip to content

fs: add mkstemp() - #66484

Merged
nodejs-github-bot merged 4 commits into
nodejs:mainfrom
marcopiraccini:fs-mkstemp
Oct 19, 2026
Merged

nodejs-github-bot merged 4 commits into
nodejs:mainfrom
marcopiraccini:fs-mkstemp

Conversation

@marcopiraccini

Copy link
Copy Markdown
Contributor

This adds an API to create a unique temporary file, the file counterpart of fs.mkdtemp():

const { path, fd } = fs.mkstempSync(prefix[, options]);
fs.mkstemp(prefix[, options], (err, { path, fd }) => {});
const { path, handle } = await fsPromises.mkstemp(prefix[, options]);

The file is created and opened in one operation, so there is no window between choosing a name and opening it. Today the alternatives are a random name with open(..., 'wx') and a hand-written retry loop, or mkdtemp() plus a file inside the directory. libuv has had uv_fs_mkstemp() since 1.34.0 and Node.js already uses it internally for the compile cache; this exposes it.

prefix and options.encoding behave as for fs.mkdtemp(): six random characters are appended, prefix may be a string, Buffer or URL, and a Buffer prefix returns a Buffer path.

Naming

The name follows the POSIX function mkstemp(3) that it wraps, in the same way as fs.mkdtemp() follows mkdtemp(3). It is also the name libuv uses (uv_fs_mkstemp()) and the name used in #33549.

History

This is the API of #33549, which was approved in 2020 but did not land and was closed in 2023 with "Anyone feel free to take this over". It stalled on the path of the error on AIX: after a failed mkstemp() the template buffer is unspecified. libuv now clears it on every platform, so the async forms would report an empty path. Here the template is saved in the request before calling libuv and used to build the error. The sync, callback and promise forms all report mkstemp '<prefix>XXXXXX', and the test checks it.

The implementation is new, since the code around it changed a lot in the meantime.

Notes for reviewers

  • Permission model: the file is opened for reading and writing, so the prefix is checked for both fs.read and fs.write, with the same helper fs.open() uses for O_RDWR | O_CREAT | O_EXCL. mkstemp is added to test-permission-fs-supported.js and to the fs-read and fs-write fixtures.
  • Workers: descriptors returned by the callback and sync forms are tracked as unmanaged descriptors, as for fs.open(), so they are closed when a worker exits and closing them does not warn. The FileHandle of the promise form manages its own descriptor.
  • vfs: mkstempSync, mkstemp and promises.mkstemp are added to VirtualFileSystem, and the node:fs functions route mounted paths to them. The hooks run after argument validation. There is no retry on a name collision, which matches the vfs mkdtemp.
  • File descriptor on failure: if encoding the resulting path fails, the descriptor is closed before the error is reported.
  • mkdtemp: the code that appends XXXXXX moved to a helper shared with mkstemp. No behavior change.
  • Warning text: the "templates ending with X are not portable" warning now names mkstemp() as well.
  • Not included: a disposable variant in the style of fs.mkdtempDisposableSync(). I would leave it for a follow-up, to keep this PR to the plain API.

Refs: #33549
Refs: #33890
Refs: #5332

Add fs.mkstemp(), fs.mkstempSync() and fsPromises.mkstemp(), which
create and open a unique temporary file in one operation. They expose
uv_fs_mkstemp(), the file counterpart of the function behind
fs.mkdtemp().

The callback and sync versions return the path and a file descriptor,
the promise version returns the path and a FileHandle.

libuv clears the path of the request when mkstemp() fails, so the
template is saved in the request and used for the path of the error.

The functions are also supported on mounted virtual file systems.

Refs: nodejs#33549
Refs: nodejs#33890
Refs: nodejs#5332
Signed-off-by: marcopiraccini <marco.piraccini@gmail.com>
@nodejs-github-bot

Copy link
Copy Markdown
Collaborator

Review requested:

  • @nodejs/security-wg

@nodejs-github-bot nodejs-github-bot added lib / src Issues and PRs involving general changes in the lib/ or src/ directories. needs-ci PRs that need a full CI run. labels Oct 3, 2026
Signed-off-by: marcopiraccini <marco.piraccini@gmail.com>
@marcopiraccini
marcopiraccini marked this pull request as ready for review October 3, 2026 16:14
@codecov

codecov Bot commented Oct 3, 2026 •

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 95.91078% with 11 lines in your changes missing coverage. Please review.
✅ Project coverage is 90.45%. Comparing base (9067cc4) to head (ad67b82).
⚠️ Report is 276 commits behind head on main.

Files with missing lines Patch % Lines
src/node_file.cc 91.35% 0 Missing and 7 partials ⚠️
src/node_file-inl.h 88.88% 0 Missing and 4 partials ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main   #66484      +/-   ##
==========================================
+ Coverage   90.42%   90.45%   +0.02%     
==========================================
  Files         790      791       +1     
  Lines      275435   276851    +1416     
  Branches    52825    53169     +344     
==========================================
+ Hits       249074   250424    +1350     
- Misses      16772    16818      +46     
- Partials     9589     9609      +20     
Files with missing lines Coverage Δ
lib/fs.js 97.36% <100.00%> (+0.03%) ⬆️
lib/internal/fs/promises.js 91.11% <100.00%> (+0.08%) ⬆️
lib/internal/fs/utils.js 96.29% <100.00%> (+<0.01%) ⬆️
lib/internal/vfs/file_system.js 99.64% <100.00%> (+0.01%) ⬆️
lib/internal/vfs/setup.js 87.52% <100.00%> (+0.39%) ⬆️
src/node_file-inl.h 85.91% <88.88%> (+0.43%) ⬆️
src/node_file.cc 75.83% <91.35%> (+0.28%) ⬆️

... and 77 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Signed-off-by: marcopiraccini <marco.piraccini@gmail.com>
@panva panva added author ready PRs with CI started, the required approvals, and no outstanding review comments. request-ci Add this label to start a Jenkins CI on a PR. Only starts once the PR has an approving review. labels Oct 5, 2026
@github-actions github-actions Bot removed the request-ci Add this label to start a Jenkins CI on a PR. Only starts once the PR has an approving review. label Oct 5, 2026
@nodejs-github-bot

Copy link
Copy Markdown
Collaborator

@aduh95

aduh95 commented Oct 7, 2026 •

Copy link
Copy Markdown
Contributor

Looks very interesting, I wonder if we could have an API such as

{
  await using fd = await mkstemp();
  await fd.writeFile(/* … */);

  await someOtherAPIThatReadsTheFile(fd);
} // file is automatically deleted on collection

I'm not sure how practical that would be, but it seems worse keeping that door open. Can you mark that API as experimental to allow it to evoluate without being semver-major?

@aduh95 aduh95 added experimental Issues and PRs related to experimental features. and removed author ready PRs with CI started, the required approvals, and no outstanding review comments. labels Oct 7, 2026
Signed-off-by: marcopiraccini <marco.piraccini@gmail.com>
@aduh95 aduh95 added author ready PRs with CI started, the required approvals, and no outstanding review comments. request-ci Add this label to start a Jenkins CI on a PR. Only starts once the PR has an approving review. labels Oct 10, 2026
@github-actions github-actions Bot removed the request-ci Add this label to start a Jenkins CI on a PR. Only starts once the PR has an approving review. label Oct 10, 2026
@nodejs-github-bot

Copy link
Copy Markdown
Collaborator

@richardlau richardlau added the semver-minor PRs that contain new features and should be released in the next minor version. label Oct 10, 2026

@gurgunday gurgunday left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lgtm

@aduh95 aduh95 added the commit-queue PRs queued for automated landing through the Commit Queue. label Oct 16, 2026
@nodejs-github-bot nodejs-github-bot added commit-queue-failed PRs whose Commit Queue landing failed and need manual intervention before retrying. and removed commit-queue PRs queued for automated landing through the Commit Queue. labels Oct 16, 2026
@nodejs-github-bot

Copy link
Copy Markdown
Collaborator

Commit Queue failed

This pull request has multiple commits, but no landing policy was selected.

Add commit-queue-squash PRs the Commit Queue should land as one squashed commit. to land it as one commit, or commit-queue-rebase PRs the Commit Queue should land as multiple self-contained commits. to land the commits separately.

The pull request was removed from the Commit Queue and labeled commit-queue-failed PRs whose Commit Queue landing failed and need manual intervention before retrying. . After resolving the failure, remove that label and add commit-queue PRs queued for automated landing through the Commit Queue. to retry.

Full Commit Queue output
�[36m⠋�[39m Loading data for nodejs/node/pull/66484
�[36m⠋�[39m Loading data for nodejs/node/pull/66484
�[36m⠋�[39m Getting collaborator contacts from README of nodejs/node
�[36m⠋�[39m Getting PR from nodejs/node/pull/66484
�[36m⠋�[39m Getting reviews from nodejs/node/pull/66484
�[36m⠋�[39m Getting comments from nodejs/node/pull/66484
�[36m⠋�[39m Getting commits from nodejs/node/pull/66484
✔  Done loading data for nodejs/node/pull/66484
----------------------------------- PR info ------------------------------------
Title      fs: add mkstemp() (#66484)
Author     Marco <marco.piraccini@gmail.com> (@marcopiraccini)
Branch     marcopiraccini:fs-mkstemp -> nodejs:main
Labels     semver-minor, lib / src, experimental, author ready, needs-ci, commit-queue
Commits    4
 - fs: add mkstemp()
 - fixup! fs: add mkstemp()
 - fixup! fs: add mkstemp()
 - fixup! fs: marked mkstemp as experimental in aip doc
Committers 1
 - marcopiraccini <marco.piraccini@gmail.com>
PR-URL: https://github.com/nodejs/node/pull/66484
Refs: https://github.com/nodejs/node/pull/33549
Refs: https://github.com/nodejs/node/issues/33890
Refs: https://github.com/nodejs/node/issues/5332
Reviewed-By: Paolo Insogna <paolo@cowtech.it>
Reviewed-By: James M Snell <jasnell@gmail.com>
Reviewed-By: Antoine du Hamel <duhamelantoine1995@gmail.com>
Reviewed-By: Gürgün Dayıoğlu <hey@gurgun.day>
------------------------------ Generated metadata ------------------------------
PR-URL: https://github.com/nodejs/node/pull/66484
Refs: https://github.com/nodejs/node/pull/33549
Refs: https://github.com/nodejs/node/issues/33890
Refs: https://github.com/nodejs/node/issues/5332
Reviewed-By: Paolo Insogna <paolo@cowtech.it>
Reviewed-By: James M Snell <jasnell@gmail.com>
Reviewed-By: Antoine du Hamel <duhamelantoine1995@gmail.com>
Reviewed-By: Gürgün Dayıoğlu <hey@gurgun.day>
--------------------------------------------------------------------------------
   ℹ  This PR was created on Sat, 03 Oct 2026 16:12:21 GMT
   ✔  Approvals: 4
   ✔  - Paolo Insogna (@ShogunPanda) (TSC): https://github.com/nodejs/node/pull/66484#pullrequestreview-5404507492
   ✔  - James M Snell (@jasnell) (TSC): https://github.com/nodejs/node/pull/66484#pullrequestreview-5405567047
   ✔  - Antoine du Hamel (@aduh95) (TSC): https://github.com/nodejs/node/pull/66484#pullrequestreview-5478492015
   ✔  - Gürgün Dayıoğlu (@gurgunday): https://github.com/nodejs/node/pull/66484#pullrequestreview-5551288332
   ✔  Last GitHub CI successful
   ℹ  Last Full PR CI on 2026-10-10T09:49:25Z: https://ci.nodejs.org/job/node-test-pull-request/78293/
�[36m⠙�[39m Querying data for job/node-test-pull-request/78293/
�[36m⠙�[39m Querying data for job/node-test-pull-request/78293/
�[36m⠙�[39m Querying API for job/node-test-pull-request/78293/
✔  Build data downloaded
   ✔  Last Jenkins CI successful
--------------------------------------------------------------------------------
   ✔  No git cherry-pick in progress
   ✔  No git am in progress
   ✔  No git rebase in progress
--------------------------------------------------------------------------------
�[36m⠹�[39m Bringing origin/main up to date...
�[36m⠹�[39m Bringing origin/main up to date...
From https://github.com/nodejs/node
 * branch                  main       -> FETCH_HEAD
✔  origin/main is now up-to-date
�[36m⠸�[39m Downloading patch for 66484
�[36m⠸�[39m Downloading patch for 66484
From https://github.com/nodejs/node
 * branch                  refs/pull/66484/merge -> FETCH_HEAD
✔  Fetched commits as 274569853742..ad67b82c03f7
--------------------------------------------------------------------------------
Auto-merging doc/api/fs.md
Auto-merging doc/api/vfs.md
Auto-merging lib/internal/fs/promises.js
Auto-merging lib/internal/vfs/file_system.js
Auto-merging test/fixtures/permission/fs-read.js
[main c8d64aa17f] fs: add mkstemp()
 Author: marcopiraccini <marco.piraccini@gmail.com>
 Date: Sat Oct 3 18:10:43 2026 +0200
 19 files changed, 742 insertions(+), 15 deletions(-)
 create mode 100644 test/parallel/test-fs-mkstemp.js
 create mode 100644 test/parallel/test-vfs-mkstemp.js
[main 1e95f1f579] fixup! fs: add mkstemp()
 Author: marcopiraccini <marco.piraccini@gmail.com>
 Date: Sat Oct 3 18:14:13 2026 +0200
 2 files changed, 48 insertions(+), 10 deletions(-)
 create mode 100644 test/parallel/test-worker-fs-mkstemp-terminate.js
Auto-merging lib/internal/fs/promises.js
[main 92d14adbfd] fixup! fs: add mkstemp()
 Author: marcopiraccini <marco.piraccini@gmail.com>
 Date: Sun Oct 4 06:30:11 2026 +0200
 5 files changed, 427 insertions(+), 120 deletions(-)
 create mode 100644 test/cctest/test_node_file_mkstemp.cc
Auto-merging doc/api/fs.md
[main 3c558c65de] fixup! fs: marked mkstemp as experimental in aip doc
 Author: marcopiraccini <marco.piraccini@gmail.com>
 Date: Sat Oct 10 09:44:23 2026 +0200
 1 file changed, 6 insertions(+)
   ✔  Patches applied
There are 4 commits in the PR. Attempting autorebase.
(node:489) [DEP0190] DeprecationWarning: Passing args to a child process with shell option true can lead to security vulnerabilities, as the arguments are not escaped, only concatenated.
(Use `node --trace-deprecation ...` to show where the warning was created)
Rebasing (2/6)
Rebasing (3/6)
Rebasing (4/6)
Executing: git node land --amend --yes
   ⚠  Found Refs: https://github.com/nodejs/node/pull/33549, skipping..
   ⚠  Found Refs: https://github.com/nodejs/node/issues/33890, skipping..
   ⚠  Found Refs: https://github.com/nodejs/node/issues/5332, skipping..
--------------------------------- New Message ----------------------------------
fs: add mkstemp()

Add fs.mkstemp(), fs.mkstempSync() and fsPromises.mkstemp(), which
create and open a unique temporary file in one operation. They expose
uv_fs_mkstemp(), the file counterpart of the function behind
fs.mkdtemp().

The callback and sync versions return the path and a file descriptor,
the promise version returns the path and a FileHandle.

libuv clears the path of the request when mkstemp() fails, so the
template is saved in the request and used for the path of the error.

The functions are also supported on mounted virtual file systems.

Refs: https://github.com/nodejs/node/pull/33549
Refs: https://github.com/nodejs/node/issues/33890
Refs: https://github.com/nodejs/node/issues/5332
Signed-off-by: marcopiraccini <marco.piraccini@gmail.com>
PR-URL: https://github.com/nodejs/node/pull/66484
Reviewed-By: Paolo Insogna <paolo@cowtech.it>
Reviewed-By: James M Snell <jasnell@gmail.com>
Reviewed-By: Antoine du Hamel <duhamelantoine1995@gmail.com>
Reviewed-By: Gürgün Dayıoğlu <hey@gurgun.day>
--------------------------------------------------------------------------------
[detached HEAD cde97f1347] fs: add mkstemp()
 Author: marcopiraccini <marco.piraccini@gmail.com>
 Date: Sat Oct 3 18:10:43 2026 +0200
 22 files changed, 1089 insertions(+), 17 deletions(-)
 create mode 100644 test/cctest/test_node_file_mkstemp.cc
 create mode 100644 test/parallel/test-fs-mkstemp.js
 create mode 100644 test/parallel/test-vfs-mkstemp.js
 create mode 100644 test/parallel/test-worker-fs-mkstemp-terminate.js
Rebasing (5/6)
Rebasing (6/6)
Executing: git node land --amend --yes
--------------------------------- New Message ----------------------------------
fixup! fs: marked mkstemp as experimental in aip doc

Signed-off-by: marcopiraccini <marco.piraccini@gmail.com>
PR-URL: https://github.com/nodejs/node/pull/66484
Refs: https://github.com/nodejs/node/pull/33549
Refs: https://github.com/nodejs/node/issues/33890
Refs: https://github.com/nodejs/node/issues/5332
Reviewed-By: Paolo Insogna <paolo@cowtech.it>
Reviewed-By: James M Snell <jasnell@gmail.com>
Reviewed-By: Antoine du Hamel <duhamelantoine1995@gmail.com>
Reviewed-By: Gürgün Dayıoğlu <hey@gurgun.day>
--------------------------------------------------------------------------------
[detached HEAD f7a57ab263] fixup! fs: marked mkstemp as experimental in aip doc
 Author: marcopiraccini <marco.piraccini@gmail.com>
 Date: Sat Oct 10 09:44:23 2026 +0200
 1 file changed, 6 insertions(+)
Successfully rebased and updated refs/heads/main.
--------------------------------------------------------------------------------
   ℹ  Add `commit-queue-squash` label to land the PR as one commit, or `commit-queue-rebase` to land as separate commits.

View workflow run

@trivikr trivikr removed the commit-queue-failed PRs whose Commit Queue landing failed and need manual intervention before retrying. label Oct 19, 2026
@trivikr trivikr added commit-queue-squash PRs the Commit Queue should land as one squashed commit. commit-queue PRs queued for automated landing through the Commit Queue. labels Oct 19, 2026
@nodejs-github-bot
nodejs-github-bot merged commit 1a885ca into nodejs:main Oct 19, 2026
88 checks passed
@nodejs-github-bot

Copy link
Copy Markdown
Collaborator

Landed in 1a885ca

@nodejs-github-bot nodejs-github-bot removed the commit-queue PRs queued for automated landing through the Commit Queue. label Oct 19, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

author ready PRs with CI started, the required approvals, and no outstanding review comments. commit-queue-squash PRs the Commit Queue should land as one squashed commit. experimental Issues and PRs related to experimental features. lib / src Issues and PRs involving general changes in the lib/ or src/ directories. needs-ci PRs that need a full CI run. semver-minor PRs that contain new features and should be released in the next minor version.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

9 participants