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

  1. Point PAPERCLIP_RUNNER_PROTOCOL_EVAL_VIEWER_DIR at the viewer build produced by the same run as the report
  2. Rebuild the Runner Lab viewer so index.html contains the hashed stylesheet link and the CSS includes .evalbook-site
  3. Verify with: grep -o 'assets/[A-Za-z0-9._-]*\.css' <viewerRoot>/index.html and grep .evalbook-site <viewerRoot>/assets/*.css
  4. 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

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


AI-assisted analysis of paperclipai/paperclip@01ad858492 (2026-09-10). Data as JSON: /api/errors/f1f0f4f6fc5f6932. Report an issue: GitHub.