Skip to content

fix: staging folders beside a project path no longer fail past 260 characters on Windows - #6492

Closed
miguel-heygen wants to merge 4 commits into
mainfrom
fix/cli-win-long-paths
Closed

miguel-heygen wants to merge 4 commits into
mainfrom
fix/cli-win-long-paths

Conversation

@miguel-heygen

@miguel-heygen miguel-heygen commented Oct 26, 2026 •

Copy link
Copy Markdown
Collaborator

What

Every staging folder the CLI, engine, producer, core and studio-server make beside a project or output path now comes from one createStagingDir in @hyperframes/core/atomic-file (a random 6-letter-or-digit suffix, the same shape mkdtemp made, and mkdirSync) instead of mkdtempSync. 26 call sites move: snapshot capture files, cloud download, registry install, template pull, measured frames, audio mixing and encoding, the URL downloader, font compression, waveform cache, HDR frames, the render transaction folder, and the distributed plan, assemble and stitch steps. A test fails on any new bare mkdtemp that is not under the system temp root; the few allowed exceptions are listed in it with the reason.

Why

On Windows, hyperframes snapshot --at 1 failed for a project whose folder path is long, with ENOENT: no such file or directory, mkdtemp '<project>\.hyperframes\artifacts\snapshots\<id>\.capture-XXXXXX'. mkdtemp on Windows does not get the long-path form that mkdir gets, so it fails once its full path passes 260 characters. render hits the same failure at the render transaction folder. Every site that stages next to a target in a project has the same failure on a deep project, so they share one helper.

Supersedes the earlier version of this change, rebuilt on current main so it also covers call sites added since. The helper keeps mkdtemp's name shape, so the patterns that recognise leftover render folders and template pull folders still match them.

Tests

  • fails without the fix: noBareMkdtemp.test.ts on main's sources lists 26 offenders (cli/src/capture/captureFile.ts:21 and 25 more) and fails; with the change it passes. atomicFile.test.ts pins the helper: private folder, 6-character suffix, a taken name is retried, five collisions give up, any other error is not retried (it fails when the suffix length is changed).
  • Both files pass 3 runs in a row (39 tests). Existing tests for the touched modules pass: engine 373, studio-server and core fonts, cli capture/cloud/registry/templates/measure/clean, producer render and distributed. Typecheck is clean in core, engine, producer, studio-server.
  • Linux cannot reproduce the 260-character limit. The Windows evidence from the earlier version (windows-latest, long paths on, 226-character project: snapshot --at 1 failed on main and exited 0 with the change; render exited 0) is in the comments; Windows CI runs on this head.

@heygengenesis heygengenesis Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Verdict: REQUEST CHANGES · 1 blocker · 0 important · 0 nits · head eebf9fe2

  • blocker — render hits the same failure on the same project. packages/producer/src/services/render/artifactTransaction.ts:149 → renderDirOwner.ts:45 still does mkdtempSync beside the output, i.e. <project>\renders\.<project>_<date>_<time>.hf-transaction-XXXXXX. Your 226-char repro project gives a ~300-char path there, and since the project name shows up twice, render breaks at a shallower depth than snapshot. Fix: move createStagingDir somewhere producer can import it (e.g. @hyperframes/core), use it in createOwnedRenderDir, and add a test there that pins it. core/src/fonts/fontCompression.ts:74 follows the same pattern but sits under the cache dir, so it's lower risk.
Root cause and scope

The root cause checks out against Node v22 src/node_file.cc: MKDir calls ToNamespacedPath (which adds the \\?\ prefix) and Mkdtemp doesn't. So every mkdtemp under a deep project path fails, not just these three.

The other mkdtemp sites in cli/producer/engine all live under tmpdir(). On win32 the render work dir also goes under tmpdir (resolveRenderWorkDirPrefix). The helper and its tests look right: 0o700, EEXIST-only retry, mkdtemp mock.

CI was still running at review time. The Windows long-path evidence comment the body promises hasn't been posted yet.

@miguel-heygen miguel-heygen changed the title fix(cli): snapshot staging folders no longer fail past 260 characters on Windows fix: staging folders beside a project path no longer fail past 260 characters on Windows Oct 26, 2026
@miguel-heygen

Copy link
Copy Markdown
Collaborator Author

Windows evidence (windows-latest, long paths enabled), same project layout, snapshot --at 1 on a project folder of 226 characters (its snapshot artifacts are about 316 characters deep): on main snapshot exits 1 with ENOENT ... mkdtemp '<project>\.hyperframes\artifacts\snapshots\<id>\.capture-XXXXXX'; with this change it exits 0 and saves both frames. At 144 characters both pass. check passes at every length, with or without the change. (A 267-character project could not be tried: Windows will not start a process with a working directory that long.)

@heygengenesis heygengenesis Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Verdict: REQUEST CHANGES — 0 resolved, 1 open, 0 new · eebf9fe → a6b0384

Comment thread packages/core/src/noBareMkdtemp.test.ts Outdated
"cli/src/media-use/lib/lut-preset-provider.mjs",
"producer/src/server.ts",
"producer/src/regression-harness.ts",
"producer/src/services/render/renderDirOwner.ts",

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

blocker (still open from the last review): renderDirOwner.ts is allowlisted as "not beside a project or output path", but it is. artifactTransaction.ts:149 calls createOwnedRenderDir(join(parent, .${stem}.hf-transaction-)) next to the render output, which is <project>\renders\.<project>_<date>_<time>.hf-transaction-XXXXXX by default. On the 226-character repro project, render still fails at renderDirOwner.ts:45 with the same mkdtemp ENOENT. Fix: have createOwnedRenderDir take (parent, prefix) and call createStagingDir, then remove this allowlist entry. Its other caller, createRenderWorkDir, already splits the prefix into dirname(prefix).

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

✅ Fixed in 7ef0c9e: the allowlist entry is gone and createOwnedRenderDir now calls createStagingDir. The new suffix mismatch is tracked in a separate thread.

@github-actions

github-actions Bot commented Oct 26, 2026 •

Copy link
Copy Markdown
Contributor

Edit accuracy: accurate 271 (base branch 271), smooth 149 of those

The gate passes.
Smoothness is reported in the artifact, not gated. A case fails only if it fails 2 of 3 runs.

Quarantined, measured but not gated (0)

Unstable (1)

  • seqplainkeys-none-pct-r0-root-z100: tracking 0.06, pressJump 0, drop 0, reload 0, render 0.03, renderKey -, undo false, teleport true / tracking 0.06, pressJump 0, drop 0, reload 0, render 0.03, renderKey -, undo true, teleport true / tracking 0.06, pressJump 0, drop 0, reload 0, render 0.03, renderKey -, undo true, teleport true

@heygengenesis heygengenesis Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Verdict: REQUEST CHANGES — 1 resolved, 0 open, 1 new · a6b0384 → 7ef0c9e

/** A render's private temp dir, stamped with the process that owns it so a later sweep can tell it was abandoned. */
export function createOwnedRenderDir(prefix: string): string {
const dir = mkdtempSync(prefix);
const dir = createStagingDir(dirname(prefix), basename(prefix));

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

blocker: createStagingDir uses an 8-hex suffix, but RENDER_TEMP_DIR (core/src/projectFolders.ts:20) only matches a 6-character mkdtemp suffix (-[A-Za-z0-9]{6}$). With this change, hf-render-0a1b2c3d, work-<uuid>-0a1b2c3d and .x.hf-transaction-0a1b2c3d all stop matching (checked in node). As a result:

  • createRenderWorkDir's sweep and hyperframes clean (listAbandonedRenderDirs) never reclaim a killed render's work dir, so its frames leak.
  • isProjectIgnoredDir stops hiding these dirs from the preview fileWatcher and projectSignature.
  • studio-server's safePath walk lists them.

Fix: widen the regex suffix to {6,8} (or pass a 6-char suffix here), and add a test that RENDER_TEMP_DIR matches what createOwnedRenderDir creates.

@miguel-heygen
miguel-heygen force-pushed the fix/cli-win-long-paths branch from 7ef0c9e to cc16106 Compare October 27, 2026 00:37
@miguel-heygen

Copy link
Copy Markdown
Collaborator Author

Windows evidence for render (windows-latest, long paths enabled): hyperframes render on a 226-character project folder with this change exits 0 and writes the video (10.0 s, rendered in 14.9 s). The reviewer reproduced the same render failing on the previous head at the transaction folder's mkdtemp.

@heygengenesis heygengenesis Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Verdict: REQUEST CHANGES — 0 resolved, 1 open, 0 new · 7ef0c9e → cc16106 (rebase only; the RENDER_TEMP_DIR 6-char suffix vs the 8-hex createStagingDir thread on renderDirOwner.ts is still unaddressed)

@miguel-heygen

Copy link
Copy Markdown
Collaborator Author

Closing: this conflicts with main, has had no update for over 5 days, and no open issue or report names who it hurts. Reopen if someone hits it; the branch is kept.

@miguel-heygen

Copy link
Copy Markdown
Collaborator Author

Superseded by #7338: GitHub would not reopen this PR after its branch was rebuilt on current main.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant