paperclipai/paperclip · error · Error
Incomplete trusted viewer build
Error message
Incomplete trusted viewer build
What it means
After collecting assets, trustedViewerFiles() performs a sanity check that the viewer build is actually usable: at least one .js asset must exist and index.html must contain a module script tag ('<script type="module"'). If either is missing, the build is considered truncated or wrong, and "Incomplete trusted viewer build" is thrown rather than publishing a shell that cannot load the report.
Source
Thrown at packages/paperclip-runner/scripts/public-eval-viewer.mjs:55
)
throw new Error("Trusted viewer must not use symlinks");
const index = await readFile(join(viewerRoot, "index.html"), "utf8");
const files = new Map();
for (const entry of await readdir(join(viewerRoot, "assets"), {
withFileTypes: true,
})) {
if (!entry.isFile() || entry.isSymbolicLink() || !ASSET.test(entry.name))
throw new Error("Unexpected trusted viewer asset");
files.set(
`viewer/assets/${entry.name}`,
await readFile(join(viewerRoot, "assets", entry.name)),
);
}
if (
![...files.keys()].some((name) => name.endsWith(".js")) ||
!index.includes('<script type="module"')
)
throw new Error("Incomplete trusted viewer build");
return { index, files };
}
export function validatePublicChatPayload(payload) {
if (
payload?.publication?.schema !== PUBLIC_CHAT_SCHEMA ||
payload.view?.sessionId !== "public-report" ||
payload.view?.composer?.state !== "disabled" ||
payload.view?.connection?.state !== "closed" ||
payload.devtools !== null
)
throw new Error(
"Public attempt must contain the read-only public chat projection",
);
const allowed = new Set([
"attemptId",
"caseId",
"disposition",View on GitHub (pinned to 01ad858492)
Solutions
- Rebuild the viewer so assets/ contains at least one .js entry and index.html keeps its <script type="module"> tag, then retry.
- Verify viewerRoot points at the built viewer output (containing index.html and assets/), not a source or placeholder directory.
- Diff index.html against a known-good built viewer to confirm the module script tag was not removed by templating or post-processing.
- If the viewer build format legitimately changed, update the '<script type="module"' check and the PUBLIC_VIEWER_DATA/publicViewerShell expectations in public-eval-viewer.mjs together.
Example fix
// before: placeholder index.html without a module script <div id="root"></div> // after: rebuild so index.html includes <script type="module" crossorigin src="./assets/index-abc123.js"></script>
Defensive patterns
Strategy: validation
Validate before calling
const index = await readFile(join(root, "index.html"), "utf8");
const hasJs = (await readdir(join(root, "assets"))).some((n) => n.endsWith(".js"));
if (!hasJs || !index.includes('<script type="module"')) throw new Error("Viewer build incomplete"); Try / catch
try {
await trustedViewerFiles(viewerRoot);
} catch (err) {
if (err.message === "Incomplete trusted viewer build") {
console.error("Viewer build at", viewerRoot, "is truncated; rerun the viewer build");
process.exit(1);
}
throw err;
} Prevention
- Build the viewer in CI and publish only from the build artifact, never from partially copied directories
- Verify the build exit code before consuming its output
- Pin and test the viewer build against the shell transformation (publicViewerShell) expectations
- Fail fast after builds with a smoke check that index.html contains a module script
When it happens
Trigger: Calling trustedViewerFiles(viewerRoot) where assets/ contains no .js file (CSS/fonts only), or where index.html was hand-edited/emptied so the '<script type="module"' substring is absent (e.g. renamed, minified differently, or the file is a placeholder).
Common situations: Partial or interrupted build output copied to viewerRoot; serving the wrong directory (an empty template instead of the built viewer); an old viewer version whose index.html format changed; someone stripping the module script during templating.
Understand the failure class
Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.
Related errors
- Unexpected trusted viewer asset
- ${label} is not a regular file at ${canonical}.
- workspace_durable_seed_invalid
- codex auth cache: account_id is not a valid account handle
- Pinned OpenCode source executable is not a regular file
AI-assisted analysis of paperclipai/paperclip@01ad858492 (2026-09-10).
Data as JSON: /api/errors/6871b34a8eafd82d.
Report an issue: GitHub.