Skip to content

Studio preview reload loop: .transcode-cache mtime touches retrigger the file watcher (shader transitions stuck at 1/5) #2951

Description

@davidemarchisio

Summary

When media.autoProxy is enabled and actually produces proxies, hyperframes preview enters a permanent reload loop. The Studio preview iframe re-navigates roughly every 700 ms, forever, with no file in the project ever changing.

The most visible symptom is that shader transitions never finish pre-capturing: the preview panel sits on "Preparing scene transitions — transition 1/5" indefinitely, and the preview is unusable. render and snapshot are unaffected, since they don't use the watcher / live-reload path.

Present in 0.7.61 through 0.7.88 (latest). Not platform-specific — reproduces on WSL2 and native Windows.

Root cause

A feedback loop between the media-proxy cache and the project file watcher.

  1. Serving a media proxy marks the cache entry as recently used, which rewrites the file's mtime (../studio-server chunk):
function markCacheEntryUsed(cachePath) {
  try {
    const now = new Date();
    utimesSync(cachePath, now, now);
  } catch {}
}
  1. The proxy cache lives inside the project directoryCACHE_DIR_NAME = ".transcode-cache".

  2. src/server/fileWatcher.ts watches the project recursively, and its exclusion list does not contain .transcode-cache:

WATCHER_EXCLUDED_DIRS = new Set([
  ".cache", ".git", ".hyperframes", ".next", ".vite",
  "build", "coverage", "dist", "node_modules", "outputs", "renders"
]);
DEBOUNCE_MS = 300;

So the mtime touch is indistinguishable from a real edit. The loop closes as:

serve proxy → utimesSync → watcher fires → SSE file-change on /api/events → Studio re-navigates the preview iframe (src + fresh _t) → composition re-requests its media → serve proxy → …

Observed period ≈ 700 ms (300 ms debounce + reload), and the server recompiles ~1.6×/sec indefinitely while otherwise idle.

Evidence

Listening directly on the SSE stream with the project completely idle — 14 events in 12 s, all for one file:

event: file-change
data: {"path":".transcode-cache/f0a349f28e3a0ff5b26294b2adbdf1305fc0c514c13dbcf27e3352ce61cd35f6.mp4"}

find . -newermt '-2 minutes' reports no changed project files during this. The cache files' mtimes are continuously reset to now.

Instrumenting shader-transition-state messages shows the shader pre-capture is not hung — it progresses normally (transition 5/5, frame 6/19 at t=33 s; a full pass needs ~33-40 s for 5 transitions at 1920×1080). Being restarted every ~700 ms, it never gets past the opening frames of transition 1, so the UI is pinned at "transition 1/5".

In DevTools this also shows up as the preview document request repeatedly reporting status (canceled) with 0.0 kB:

preview?__hf_shader_capture_scale=1&__hf_shader_loading=player

Each reload supersedes the in-flight navigation. Sub-resources (frame captures, media, fonts) in the same batch complete with 200/206, which makes it look like one specific request is failing when it's actually the iframe's own navigation being cancelled over and over.

Reproduction

  1. A project with "media": { "autoProxy": true } and video assets that get proxied (e.g. VP9 .webm).
  2. npx hyperframes preview
  3. curl -sN http://localhost:<port>/api/events — observe a continuous stream of file-change events for .transcode-cache/*.mp4 with nothing being edited.
  4. With shader transitions configured via HyperShader.init(...), the preview panel stays on "Preparing scene transitions — transition 1/5" forever.

Suggested fix

Add .transcode-cache to WATCHER_EXCLUDED_DIRS in src/server/fileWatcher.ts. .thumbnails looks like it has the same exposure — it's excluded in walkFiles/IGNORE_DIRS elsewhere but is likewise absent from the watcher list.

Alternatives worth considering: move the proxy cache out of the project tree, or skip the utimesSync keep-alive for entries whose mtime is already recent (it only needs LRU granularity, not per-request precision).

Workaround

Set "media": { "autoProxy": false } and delete .transcode-cache/. This is only viable when the sources are natively decodable by Chrome.

Worth noting for projects using transparent video: the proxy transcodes VP9/WebM to H.264 .mp4, which drops the alpha channel — so for transparent overlays the proxy is also lossy, independent of this bug.

Environment

  • hyperframes 0.7.87 and 0.7.88
  • Node v24.15.0
  • WSL2 (Linux 6.18) — also reported on native Windows
  • FFmpeg present, Docker not installed

Activity

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

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions