Skip to content

fix(core): fsync atomic writes and keep an unreadable video state recoverable - #7484

Open
aniruddhaadak80 wants to merge 1 commit into
heygen-com:mainfrom
aniruddhaadak80:fix/video-state-durability
Open

aniruddhaadak80 wants to merge 1 commit into
heygen-com:mainfrom
aniruddhaadak80:fix/video-state-durability

Conversation

@aniruddhaadak80

Copy link
Copy Markdown

Summary

video-state.json could come back as a file full of NUL bytes after a hard power loss, and the next run silently replaced it with an empty state — losing every check, snapshot, revision and file record for that production.

Two causes, both fixed here:

  1. The atomic write was not durable. writeTempSibling wrote the temp file with writeFileSync(..., { flag: "wx" }) and publishSibling renamed it over the target. Nothing fsynced the data before the rename, so the name could reach the disk before the bytes did — a power cut left the target with the new size and zero-filled contents.
  2. A read failure reseeded. updateState caught SyntaxError, moved the file aside to .invalid-<uuid> and continued with an empty state.

Fix

packages/core/src/atomicFile.ts

  • writeTempSibling now writes through a file descriptor and fsyncs it before the rename, so the rename can never outrun the data: a power cut leaves only an unreferenced temp file, never a NUL-filled target.
  • createDirectly fsyncs the created file as well, and the parent directory is synced after a sibling publish (skipped where the platform cannot sync a directory).
  • The fs surface used by these helpers now carries openSync/fsyncSync/closeSync.

packages/studio-server/src/history/videoState.ts

  • A state file that cannot be parsed is never silently replaced. The damage is reported through a new VideoStateCorruptError that names the state path, the backup path and the recovery steps.
  • Each successful publish keeps the last published state at .hyperframes/video-state.json.bak.
  • On a corrupt state the backup is restored over it, the damaged bytes are copied aside as .invalid-<uuid>, and a warning names the copy. With no readable backup the update fails instead of rewriting anything, so the records are still there to recover.

Tests

  • packages/core: vitest run src/atomicFile.test.ts → 32 passed, 8 skipped. New cases cover fsync-before-rename ordering, directory sync, and the direct-create path.
  • packages/studio-server: vitest run src/history/videoState.test.ts → 11 passed, 1 failed; the failure is the pre-existing Windows symlink EPERM case (it fails identically on the unmodified parent commit).
  • tsc --noEmit clean in packages/core and packages/studio-server.

Closes #7417

Copilot AI balanced review requested due to automatic review settings November 10, 2026 08:01

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Copilot was unable to review this pull request because the user who requested the review has insufficient remaining quota. Try requesting a review at a lower effort level.

This branch has not been deployed

No deployments
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.

video-state.json is wiped after a power loss: atomic write has no fsync, and a parse error reseeds empty state

2 participants