paperclipai/paperclip · error · ValueError

History has no latest campaign

Error message

History has no latest campaign

What it means

After schema validation, latest() reads history['latestCampaignId'] to know which campaign the published report should show. If the field is missing, null, or empty, there is no authoritative 'latest' pointer, and rather than silently falling back to an arbitrary campaign the script raises ValueError.

Solutions

  1. Re-run the campaign publisher so history.json includes a non-empty latestCampaignId.
  2. If this is a legitimate no-campaigns-yet state, guard the build: skip rendering (or emit an empty page) until a campaign exists.
  3. Fix the writer that publishes history to always set latestCampaignId atomically with campaigns.
  4. When testing offline, add latestCampaignId to your fixture matching one of campaigns[].campaignId.

Example fix

# before
history = {"schema": ..., "campaigns": [...]}  # no latestCampaignId
# after
history = {"schema": ..., "latestCampaignId": "c-2026-09-18", "campaigns": [...]}
Defensive patterns

Strategy: validation

Validate before calling

def has_latest(history):
    return bool(history.get("latestCampaignId"))

Type guard

def has_latest_campaign(history):
    return isinstance(history.get("latestCampaignId"), str) and history["latestCampaignId"] != ""

Try / catch

try:
    values = summarize(history, kind)
except ValueError as e:
    if str(e) == "History has no latest campaign":
        return None  # no campaigns published yet; render empty page
    raise

Prevention

When it happens

Trigger: A history JSON generated before any campaign ran (campaigns list empty or pointer never set), a truncated/partial upload of history.json, or a writer bug that omits latestCampaignId while still listing campaigns.

Common situations: Freshly initialized eval bucket with no campaigns yet; publisher crashed mid-update leaving history without the pointer; manually constructed fixtures for tests that forgot latestCampaignId.

Related errors


AI-assisted analysis of paperclipai/paperclip@3f1d897a7c (2026-09-18). Data as JSON: /api/errors/3ac55890b186dd42. Report an issue: GitHub.

Appendix: source

Thrown at scripts/evals-hub/build.py:24

import json
from pathlib import Path
from string import Template
from urllib.parse import quote, urlsplit
from urllib.request import urlopen

ORIGIN = "https://d1p6rlowie26tp.cloudfront.net"
SYSTEMS = {
    "runner": ("runner-protocol-evals", "paperclip.runner-protocol-eval.history/v1"),
    "product": ("runner-e2e", "paperclip.runner-e2e.history/v1"),
}


def latest(history, schema):
    if history.get("schema") != schema:
        raise ValueError("Unsupported history schema")
    campaign_id = history.get("latestCampaignId")
    if not campaign_id:
        raise ValueError("History has no latest campaign")
    candidates = [c for c in history["campaigns"] if
                  c["campaignId"] == campaign_id or
                  c.get("reportRevision", {}).get("sourceCampaignId") == campaign_id]
    if not candidates:
        raise ValueError("Latest campaign is absent from history")
    # Report refreshes replace presentation, never the measurement date.
    return max(candidates, key=lambda c: c.get("reportRevision", {}).get("renderedAt", c["generatedAt"]))


def public_report(url, prefix):
    parsed = urlsplit(url)
    if (parsed.scheme != "https" or parsed.netloc != urlsplit(ORIGIN).netloc
            or not parsed.path.startswith(f"/{prefix}/campaigns/")
            or parsed.query or parsed.fragment):
        raise ValueError("Unexpected public report URL")
    return html.escape(url, quote=True)

View on GitHub (pinned to 3f1d897a7c)