Skip to content

@remotion/web-renderer: release completed render results from queues - #12631

Merged
JonnyBurger merged 2 commits into
remotion-dev:mainfrom
alec-watts:codex/release-render-queue-results
Oct 29, 2026
Merged

JonnyBurger merged 2 commits into
remotion-dev:mainfrom
alec-watts:codex/release-render-queue-results

Conversation

@alec-watts

Copy link
Copy Markdown
Contributor

The module-global still and media queues store the actual render promise. Once settled, that promise keeps its last result or rejection reason alive until another render replaces it. A still result includes its canvas and internal renderer state, so an idle queue can unnecessarily keep those resources reachable after the caller is done.

Keep a completion-only tail in each existing queue and return the original render promise to callers. The tail resolves to undefined on both success and failure; caller results and rejection identities remain intact. The queue types now infer Promise<void>, and the result casts are no longer needed. No public API changes.

Validation

  • bun run makewebrenderer: all 13 workspace build tasks pass, including TypeScript and production bundles.
  • bunx vitest run src/test/render-queue-result.test.tsx src/test/cancel-render.test.tsx --browser chromium: 3 tests pass using real still and media renders. They check that queue tails contain no completed payload, results remain usable after a subsequent queued render, and the original cancellation error is preserved.
  • Reverting the three implementation files makes all 3 tests fail; the candidate files were restored afterward.
  • Formatting passes. Targeted ESLint has no errors and two existing TODO warnings in render-media-on-web.tsx.
  • A downstream optimized Chromium workload also verified collection of the actual still-result and canvas wrappers after the caller releases them. This does not quantify native/GPU memory savings or claim that all other project-state owners are released.

Why each file changes

File under packages/web-renderer/src/ Reason
render-still-on-web.tsx Keep completion in the existing still queue while returning the original render promise.
render-media-on-web.tsx Apply the same ownership rule to the media queue.
render-operations-queue.ts Infer completion-only tail types.
test/cancel-render.test.tsx Extend the existing error-identity regression to check that queue tails release rejected errors.
test/render-queue-result.test.tsx Exercise successful queue completion and retained caller-result usability with real still/media renders.

Diff cleanup found no formatting-only changes, parallel mechanisms or unused exports.

@vercel

vercel Bot commented Oct 26, 2026 •

Copy link
Copy Markdown
Contributor

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
bugs Ready Ready Preview Oct 29, 2026 11:27am UTC
remotion Ready Ready Preview Oct 29, 2026 11:27am UTC

Request Review

@pullfrog pullfrog 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.

✅ No new issues found.

Reviewed changes

  • Still queue completion tail — render-still-on-web.tsx returns the original render promise to callers and stores a completion-only tail (render.then(() => {}, () => {})) in onlyOneStillRenderAtATimeQueue.
  • Media queue completion tail — the same ownership change applied to onlyOneMediaRenderAtATimeQueue in render-media-on-web.tsx.
  • Queue typing — render-operations-queue.ts drops the as Promise<unknown> casts so ref infers Promise<void>; the return casts are no longer needed.
  • Tests — cancel-render.test.tsx asserts a rejected render leaves an undefined-resolving queue tail while preserving the original error identity; new render-queue-result.test.tsx covers successful still/media renders, tail settling to undefined, and caller results remaining usable after a subsequent queued render.

Verified locally: ESLint clean (only the two pre-existing TODO warnings in render-media-on-web.tsx), oxfmt --check passes, and tsc --noEmit passes. A spec- and V8-backed check confirmed serialization is preserved (two synchronous calls still run strictly end-to-end), the queue no longer retains the settled still result/canvas/internalState, and caller result/rejection identities are intact.

ℹ️ The completion tail also marks the returned promise as handled, silencing ignored render failures

The internal render.then(() => {}, () => {}) attaches a rejection handler to the same promise returned to callers, so a caller that ignores a failing render (and queues no further render) will no longer trigger an unhandledrejection event — the base code surfaced it because queue.ref and the returned promise carried no handler. Callers that await or .catch() the result are unaffected. This is inherent to observing completion from the queue (any observer marks the source handled), so it is likely an acceptable trade-off; raising it only because the PR body states rejection identities stay intact without noting the change in global surfacing.

Technical details
# Completion tail suppresses unhandledrejection for ignored renders

## Affected sites
- `packages/web-renderer/src/render-still-on-web.tsx:285` — the tail attaches a rejection handler to the promise returned at line 290.
- `packages/web-renderer/src/render-media-on-web.tsx:900` — same for the promise returned at line 905.

## Required outcome
- None required. Preserving global surfacing would mean not attaching a handler to the returned promise, which conflicts with tracking completion, so this is effectively a design trade-off.

## References
- `Promise.then` marks the source promise as handled when a rejection handler is present (`HostPromiseRejectionTracker`): https://tc39.es/ecma262/multipage/control-abstraction-objects.html#sec-host-promise-rejection-tracker
- `unhandledrejection` fires only for promises with no rejection handler: https://developer.mozilla.org/en-US/docs/Web/API/Window/unhandledrejection_event

Pullfrog  | View workflow run | Using deepseek-v4.1-flash | 𝕏

# Conflicts:
#	packages/web-renderer/src/render-still-on-web.tsx
@JonnyBurger JonnyBurger changed the title fix(web-renderer): release completed render results from queues @remotion/web-renderer: release completed render results from queues Oct 29, 2026
@JonnyBurger

Copy link
Copy Markdown
Member

Thanks a lot!

@JonnyBurger
JonnyBurger merged commit 965f3f5 into remotion-dev:main Oct 29, 2026
18 checks passed

This branch was successfully deployed

2 active deployments
Preview – remotion — 83cc50a6 Deployed Oct 29, 2026 by vercel[bot]
Preview – bugs — 83cc50a6 Deployed Oct 29, 2026 by vercel[bot]
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.

2 participants