paperclipai/paperclip · error · ValueError
Latest campaign is absent from history
Error message
Latest campaign is absent from history
What it means
latest() filters history['campaigns'] for entries matching latestCampaignId either directly (campaignId) or via a reportRevision whose sourceCampaignId points at it. If no entry matches, the pointer references a campaign the list does not contain — an inconsistent feed — and the script raises rather than falling back to an arbitrary run.
Solutions
- Re-publish history.json so the campaigns list contains the campaign named by latestCampaignId (or its reportRevision sourceCampaignId).
- Fix the publisher/retention logic to update or clear latestCampaignId whenever campaigns are pruned.
- If a stale pointer is expected, regenerate the pointer to the newest retained campaignId.
- Correct the campaignId in your fixture so it matches an entry in campaigns.
Example fix
# before
{"latestCampaignId": "c-42", "campaigns": [{"campaignId": "c-41", ...}]}
# after
{"latestCampaignId": "c-41", "campaigns": [{"campaignId": "c-41", ...}]} Defensive patterns
Strategy: validation
Validate before calling
def latest_is_present(history):
cid = history.get("latestCampaignId")
return any(c.get("campaignId") == cid or
c.get("reportRevision", {}).get("sourceCampaignId") == cid
for c in history.get("campaigns", [])) Type guard
def pointer_resolves(history):
cid = history.get("latestCampaignId")
return isinstance(cid, str) and any(
c.get("campaignId") == cid or c.get("reportRevision", {}).get("sourceCampaignId") == cid
for c in history.get("campaigns", [])) Try / catch
try:
values = summarize(history, kind)
except ValueError as e:
if str(e) == "Latest campaign is absent from history":
history = refetch_history() # pointer/list mismatch; re-download consistent feed
values = summarize(history, kind)
else:
raise Prevention
- Update or clear latestCampaignId whenever a retention job prunes campaigns.
- Never hand-edit campaignId strings in fixtures; generate ids programmatically.
- Re-fetch the feed when a pointer/list mismatch is detected instead of proceeding.
- Add an integrity check post-publish: pointer must resolve within the campaigns list.
When it happens
Trigger: latestCampaignId names a campaign that was pruned from the campaigns list (retention/truncation), a reportRevision references a sourceCampaignId that was dropped, or the id was typo'd in a hand-edited fixture.
Common situations: Retention job trimming old campaigns without updating latestCampaignId; partial history upload; fixtures edited by hand where campaignId strings don't match.
Understand the failure class
Background: Record Not Found Errors: "not found", RecordNotFound, and "was not found" — what they mean and how to fix them — this error's family across 28 libraries.
Related errors
- History has no latest campaign
- Invalid campaign counts
- Unsupported history schema
- History date must include a timezone
- Missing campaign coverage flag
AI-assisted analysis of paperclipai/paperclip@3f1d897a7c (2026-09-18).
Data as JSON: /api/errors/369e568b12814c93.
Report an issue: GitHub.
Appendix: source
Thrown at scripts/evals-hub/build.py:29
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)
def display_date(value):
date = datetime.fromisoformat(value.replace("Z", "+00:00"))
if date.tzinfo is None:
raise ValueError("History date must include a timezone")
return date.astimezone(timezone.utc).strftime("%d %b %Y · %H:%M UTC")View on GitHub (pinned to 3f1d897a7c)