{"record":{"id":"6871b34a8eafd82d","repo":"paperclipai/paperclip","slug":"incomplete-trusted-viewer-build","errorCode":null,"errorMessage":"Incomplete trusted viewer build","messagePattern":"Incomplete trusted viewer build","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/paperclip-runner/scripts/public-eval-viewer.mjs","lineNumber":55,"sourceCode":"  )\n    throw new Error(\"Trusted viewer must not use symlinks\");\n  const index = await readFile(join(viewerRoot, \"index.html\"), \"utf8\");\n  const files = new Map();\n  for (const entry of await readdir(join(viewerRoot, \"assets\"), {\n    withFileTypes: true,\n  })) {\n    if (!entry.isFile() || entry.isSymbolicLink() || !ASSET.test(entry.name))\n      throw new Error(\"Unexpected trusted viewer asset\");\n    files.set(\n      `viewer/assets/${entry.name}`,\n      await readFile(join(viewerRoot, \"assets\", entry.name)),\n    );\n  }\n  if (\n    ![...files.keys()].some((name) => name.endsWith(\".js\")) ||\n    !index.includes('<script type=\"module\"')\n  )\n    throw new Error(\"Incomplete trusted viewer build\");\n  return { index, files };\n}\n\nexport function validatePublicChatPayload(payload) {\n  if (\n    payload?.publication?.schema !== PUBLIC_CHAT_SCHEMA ||\n    payload.view?.sessionId !== \"public-report\" ||\n    payload.view?.composer?.state !== \"disabled\" ||\n    payload.view?.connection?.state !== \"closed\" ||\n    payload.devtools !== null\n  )\n    throw new Error(\n      \"Public attempt must contain the read-only public chat projection\",\n    );\n  const allowed = new Set([\n    \"attemptId\",\n    \"caseId\",\n    \"disposition\",","sourceCodeStart":37,"sourceCodeEnd":73,"githubUrl":"https://github.com/paperclipai/paperclip/blob/01ad8584922b5d85292b1723cae71fa0d9b07a19/packages/paperclip-runner/scripts/public-eval-viewer.mjs#L37-L73","documentation":"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.","triggerScenarios":"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).","commonSituations":"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.","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."],"exampleFix":"// before: placeholder index.html without a module script\n<div id=\"root\"></div>\n// after: rebuild so index.html includes\n<script type=\"module\" crossorigin src=\"./assets/index-abc123.js\"></script>","handlingStrategy":"validation","validationCode":"const index = await readFile(join(root, \"index.html\"), \"utf8\");\nconst hasJs = (await readdir(join(root, \"assets\"))).some((n) => n.endsWith(\".js\"));\nif (!hasJs || !index.includes('<script type=\"module\"')) throw new Error(\"Viewer build incomplete\");","typeGuard":null,"tryCatchPattern":"try {\n  await trustedViewerFiles(viewerRoot);\n} catch (err) {\n  if (err.message === \"Incomplete trusted viewer build\") {\n    console.error(\"Viewer build at\", viewerRoot, \"is truncated; rerun the viewer build\");\n    process.exit(1);\n  }\n  throw err;\n}","preventionTips":["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"],"tags":["validation","build-artifacts","filesystem"],"backgroundTag":"internal-invariant-violation","analyzedSha":"01ad8584922b5d85292b1723cae71fa0d9b07a19","analyzedAt":"2026-09-10T03:14:50.855Z","contentChangedAt":"2026-09-10T03:14:50.855Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}