Repository navigation
Description
Describe the bug
A project can use ../shared/poster%20art.svg successfully locally, then silently lose the poster when it is packed for publish or cloud rendering. The archive localizer looks for a literal %20 filename rather than poster art.svg. Unicode escapes fail the same way.
Minimal reproduction
From a built repository checkout, create a synthetic project and sibling poster:
mkdir -p archive-repro/project/assets archive-repro/shared
ffmpeg -y -f lavfi -i color=c=black:s=320x180:d=1 -an -c:v libx264 -pix_fmt yuv420p archive-repro/project/assets/clip.mp4
cat > 'archive-repro/shared/poster art.svg' <<'SVG'
<svg xmlns="http://www.w3.org/2000/svg" width="320" height="180"><path fill="#00ff00" d="M0 0h320v180H0z"/></svg>
SVG
cat > archive-repro/project/index.html <<'HTML'
<!doctype html><html lang="en"><head><meta charset="UTF-8"><title>External poster</title>
<style>html,body{margin:0;width:100%;height:100%}#root{width:100%;height:100%}video{width:100%;height:100%;object-fit:cover}</style></head><body>
<div id="root" data-composition-id="poster-probe" data-no-timeline data-width="320" data-height="180" data-duration="1">
<video class="clip" data-start="0" data-duration="1" data-track-index="0" muted playsinline src="assets/clip.mp4" poster="../shared/poster%20art.svg"></video>
</div></body></html>
HTMLUse the same staging helper consumed by publish and cloud render to inspect the ZIP without uploading:
bun --eval 'import { writeFileSync } from "node:fs"; import { createPublishArchive } from "./packages/cli/src/utils/publishProject.ts"; const archive = createPublishArchive("archive-repro/project"); writeFileSync("archive-repro/project.zip", archive.buffer); console.log(archive.fileCount);'
unzip -l archive-repro/project.zipSteps to reproduce
- Open the original project. The green poster loads.
- Build the archive. Move and unpack it away from the original sibling
sharedfolder. - Open the relocated project. The poster is missing and returns 404. A plain
poster.svgcontrol is copied and keeps working.
The real cloud render <project> --dry-run --json reports the same two-file archive for the encoded case, with no error.
Expected behavior
The archive includes the sibling poster and rewrites its reference to a URL that resolves to that copied file.
Actual behavior
Only index.html and assets/clip.mp4 are included. The HTML retains the unresolved ../shared/poster%20art.svg. Local files with spaces and non-ASCII names are valid and load before packaging.
Environment
Reproduced on main f21c3e3e7e8387ec9638cac2179f39c630ffabcf, Linux x64, Bun 1.4.4, Node 26.11.1, FFmpeg n9.0.2. Relocated browser checks used Chrome Headless Shell 152.0.7928.2.
The integrity-verified published [email protected] archive helper also omits encoded external src assets. That release predates #5309's poster scanning, so its plain poster behavior is not a passing control for this report.
Additional context
#1160 introduced external asset localization and #5309 added poster scanning. This is a URL decoding failure in the existing resolver, distinct from proxy baking and media offloading. No authenticated publish or cloud upload was needed to reproduce it.
Activity
miguel-heygen commentedon Oct 25, 2026
Thanks for the detailed report. I can reproduce this with the published 0.8.193:
hyperframes cloud render <project> --dry-run --jsonpacks onlyindex.htmlwhen the image is referenced as../shared/poster%20art.svg, while the same setup with a plain file name also packs_ext/shared/plain.svg.The cause is
tryResolveExternalinpackages/cli/src/utils/publishProject.ts. It resolves the raw attribute value as a file path without decoding it, so it looks for a file literally namedposter%20art.svg, finds nothing, and leaves the reference unchanged. Bothpublishandcloud renderbuild their archive through that function.#6266 fixes it there, so that PR is the one to follow. A related case it does not cover yet: an external reference with a query or fragment, such as
poster.svg?v=2oricons.svg#play, is also left out of the archive. I've noted that on the PR.