fix(producer): fall back to copying extracted frames when symlink hits EPERM [P1] - #1959
Merged
Merged
Conversation
…s EPERM materializeExtractedFramesForCompiledDir stages each video's extracted frames into the compiled dir via a single symlink (the in-process renderer's default; distributed plan() already copies via materializeSymlinks). Windows without Developer Mode (or Administrator) cannot create symlinks and rejects with EPERM, so high/standard-quality renders failed there — while draft quality worked because it avoids the symlinked-cache path entirely. Fix: a new stageExtractedFrameDir helper catches EPERM/EACCES from symlinkSync and falls back to the same recursive cpSync the materializeSymlinks path already uses. The extra disk is far better than a hard render failure on a default Windows configuration. Non-permission errors (ENOSPC, etc.) still propagate so real failures aren't masked as silent copies. Extracting the helper also keeps the main function under the complexity gate. Test: two new cases via the injected fileSystem — symlinkSync throwing EPERM triggers exactly one recursive cpSync (frames still remapped under compiledDir), and a non-permission error (ENOSPC) rethrows without falling back to copy. Full renderOrchestrator suite (81) passes.
This was referenced Jul 5, 2026
Follow-up to this PR's EPERM copy fallback, from a further Windows report: the symlink fails with EEXIST after the extraction cache is GC'd. A prior render's symlink at the compiled linkPath dangles once its target is removed; the caller's existsSync() guard follows the dead link and reads it as absent, so staging runs again, but the link file still exists and symlinkSync collides with EEXIST -> the render hard-fails. Catch EEXIST in stageExtractedFrameDir, clear the stale entry (rmSync), and re-stage (link, or copy on EPERM/EACCES). Factored the link-or-copy into a helper reused by both the first attempt and the retry. rmSync is an optional injected fs method (default fs supplies it; only the EEXIST path calls it). New unit test covers the dangling-symlink recovery.
Collaborator
Author
|
Added a follow-up commit for the EEXIST variant of the same failure, from a further Windows report: after the extraction cache is GC'd, a prior render's frame-symlink at the compiled |
miguel-heygen
marked this pull request as ready for review
July 7, 2026 19:04
…copy path Addresses review nits on the frame-staging fallback: - Widen the symlink no-privilege catch from EPERM/EACCES to also include UNKNOWN (some Windows builds surface a symlink privilege denial as UNKNOWN). - Wrap the EEXIST stale-entry recovery around BOTH staging branches, not just the symlink one: after #2025 Windows uses the eager cpSync path, which can collide with a dangling symlink left by a prior Linux run — now it clears the stale entry and re-stages either way. - Emit a one-time INFO log when symlinking degrades to copying, so a heavier Windows render is self-explanatory.
Collaborator
Author
|
@tai thanks — addressed the nits in 5160b0e:
|
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.
Root cause
materializeExtractedFramesForCompiledDirstages each video's extracted frames into the compiled dir via a single symlink (the in-process renderer's default; distributedplan()already copies viamaterializeSymlinks). Windows without Developer Mode (or Administrator) cannot create symlinks and rejects withEPERM, so high/standard-quality renders failed there — while draft quality worked because it avoids the symlinked-cache path entirely (the reporter's exact observation).Developer Mode is off by default on Windows, so this hits a common configuration.
Fix
A new
stageExtractedFrameDirhelper catchesEPERM/EACCESfromsymlinkSyncand falls back to the same recursivecpSyncthematerializeSymlinkspath already uses. The extra disk is far better than a hard render failure. Non-permission errors (ENOSPC, etc.) still propagate so real failures aren't masked as silent copies. (Extracting the helper also keeps the caller under fallow's complexity gate.)Test plan
Two new cases via the injected
fileSystem:symlinkSyncthrowingEPERMtriggers exactly one recursivecpSync, with frames still remapped undercompiledDir;ENOSPC) rethrows without falling back to copy.Full
renderOrchestrator.test.tssuite (81 tests) passes; producer build + typecheck clean;oxlint/oxfmtclean.(Note: the earlier "webm alpha broken on Windows" report from a prior batch was resolved by a later reporter as an ffmpeg-default-decoder confusion, not a code bug — webm VP9 alpha renders correctly; consumers must decode with
-c:v libvpx-vp9. Logged as a docs-callout follow-up.)