paperclipai/paperclip · error · Error
Published history requires the same-run Runner Lab site them
Error message
Published history requires the same-run Runner Lab site theme
What it means
publishProtocolEvalHistory renders the public history index using the same stylesheet as the Runner Lab viewer build for the current run. It extracts the hashed CSS href from the trusted viewer's index.html and requires the CSS to contain the .evalbook-site theme class. A missing link tag, missing stylesheet file, or unthemed build means the published history page would not match the campaign pages, so publication is refused.
Source
Thrown at packages/paperclip-runner/scripts/publish-runner-protocol-eval-history.mjs:461
resolve(reportRoot, "campaign.json"),
cacheControl,
);
}
export async function publishProtocolEvalHistory({
reportRoot,
destination,
viewerRoot,
}) {
const validatedDestination =
validateProtocolEvalHistoryDestination(destination);
const { campaign } = await validatePublicProtocolEvalReport(reportRoot, {
viewerRoot,
});
const viewer = await trustedViewerFiles(viewerRoot);
const stylesheet = viewer.index.match(/<link rel="stylesheet" crossorigin href="\.\/(assets\/[A-Za-z0-9._-]+\.css)">/)?.[1];
if (!stylesheet || !viewer.files.get(`viewer/${stylesheet}`)?.includes(".evalbook-site"))
throw new Error("Published history requires the same-run Runner Lab site theme");
const stylesheetHref = `campaigns/${campaign.campaignId}/viewer/${stylesheet}`;
const manifest = await createProtocolEvalBundleManifest(
reportRoot,
campaign.campaignId,
{ viewerRoot },
);
const temporary = await mkdtemp(
join(tmpdir(), "runner-protocol-eval-history-"),
);
const historyKey = `${validatedDestination.prefix}/history.json`;
const mergedHistory = mergeProtocolEvalHistory(
(await downloadJson(
validatedDestination.bucket,
historyKey,
join(temporary, "history.json"),
)) ?? emptyProtocolEvalHistory(),
protocolEvalHistoryRecord(
campaign,View on GitHub (pinned to 01ad858492)
Solutions
- Point PAPERCLIP_RUNNER_PROTOCOL_EVAL_VIEWER_DIR at the viewer build produced by the same run as the report
- Rebuild the Runner Lab viewer so index.html contains the hashed stylesheet link and the CSS includes .evalbook-site
- Verify with: grep -o 'assets/[A-Za-z0-9._-]*\.css' <viewerRoot>/index.html and grep .evalbook-site <viewerRoot>/assets/*.css
- Regenerate both the report and viewer together so their revisions match
Example fix
// before PAPERCLIP_RUNNER_PROTOCOL_EVAL_VIEWER_DIR=./old-viewer-dist pnpm publish-eval-history // after pnpm --filter @paperclipai/runner build:viewer # emits viewer/ with .evalbook-site CSS PAPERCLIP_RUNNER_PROTOCOL_EVAL_VIEWER_DIR=./viewer pnpm publish-eval-history
Defensive patterns
Strategy: validation
Validate before calling
const viewer = await trustedViewerFiles(viewerRoot);
const css = viewer.index.match(/<link rel="stylesheet" crossorigin href="\.\/(assets\/[A-Za-z0-9._-]+\.css)">/)?.[1];
if (!css || !viewer.files.get(`viewer/${css}`)?.includes(".evalbook-site"))
throw new Error("viewer build lacks the Runner Lab theme; rebuild it"); Type guard
const hasLabTheme = (viewer) => {
const css = viewer.index.match(/\.\/(assets\/[A-Za-z0-9._-]+\.css)/)?.[1];
return !!css && viewer.files.get(`viewer/${css}`)?.includes(".evalbook-site") === true;
}; Try / catch
try {
await publishProtocolEvalHistory({ reportRoot, destination, viewerRoot });
} catch (e) {
if (String(e.message).includes("same-run Runner Lab site theme")) {
console.error("rebuild the viewer for this run and point viewerRoot at its output");
}
throw e;
} Prevention
- Build the viewer and the report in the same workflow run and pass that exact viewerRoot
- Set PAPERCLIP_RUNNER_PROTOCOL_EVAL_VIEWER_DIR explicitly per run; never reuse a stale dist
- CI-check that the viewer CSS contains .evalbook-site before publishing
When it happens
Trigger: viewerRoot pointing at a stale or wrong build whose index.html lacks the expected <link rel="stylesheet" crossorigin href="./assets/*.css"> tag; a viewer build without the .evalbook-site theme class in its CSS; viewerRoot undefined so trustedViewerFiles cannot resolve the run's viewer.
Common situations: Setting PAPERCLIP_RUNNER_PROTOCOL_EVAL_VIEWER_DIR to an older viewer output; viewer rebuilt between report generation and publish so the hashed asset name changed; a non-Runner Lab viewer variant used by mistake.
Understand the failure class
Background: "Invalid value" and "allowed values are" config errors: what your library rejected and how to fix it — this error's family across 41 libraries.
Related errors
- Unexpected trusted viewer asset
- Incomplete trusted viewer build
- Chat Evalbook must not mix in legacy plain attempt pages
- Report campaign ID does not match publication target
- Immutable campaign history changed for ${record.campaignId}
AI-assisted analysis of paperclipai/paperclip@01ad858492 (2026-09-10).
Data as JSON: /api/errors/f1f0f4f6fc5f6932.
Report an issue: GitHub.