paperclipai/paperclip · error · Error
Uploaded, but public verification did not finish. Check
Error message
Uploaded, but public verification did not finish. Check ${url} and the CloudFront cache policy (minimum TTL must not exceed 300 seconds). What it means
After uploads, the script retries public verification (HEAD checks on assets and current.json) up to 22 attempts 15 seconds apart. If no attempt succeeds, uploads are already done but the public URLs never verified, so this final error is thrown pointing at the URL and the CloudFront cache policy (minimum TTL must not exceed 300s).
Solutions
- Confirm the CloudFront cache policy minimum TTL is <= 300 seconds so current.json (max-age=300) expires promptly
- Manually create a CloudFront invalidation for the publish prefix and re-run verification or the script
- Check curl -I <url> and the asset HEAD responses to see whether failures are 404 (propagation/origin) vs wrong headers (policy)
- Verify PAPERCLIP_PAGE_BASE_URL and PAPERCLIP_PAGE_BUCKET point at the host/bucket actually backed by the CDN distribution
Example fix
// before CachePolicy: minTTL=3600 on current.json // after CachePolicy: minTTL=0, defaultTTL=300 for current.json; rerun publish script
Defensive patterns
Strategy: retry
Validate before calling
const head = await fetch(url, { method: "HEAD" });
if (!head.ok) console.warn(`current.json not yet verifiable: ${head.status}`); Try / catch
try { await main(); } catch (e) { if (e.message.includes("public verification did not finish")) { /* uploads done: check CDN TTL/policy, invalidate cache, re-verify */ } } Prevention
- Keep the CloudFront minimum TTL at or below 300 seconds so current.json (max-age=300) refreshes
- Ensure PAPERCLIP_PAGE_BASE_URL and PAPERCLIP_PAGE_BUCKET resolve to the same CDN-backed origin
- Pre-create cache invalidations for the publish prefix in CD pipelines
- Monitor the CDN for 4xx/5xx on the announcement prefix to catch policy misconfigurations early
When it happens
Trigger: All ~5.5 minutes of verification attempts fail because the CDN never serves the new objects with correct headers/content-type, or current.json never reflects the new manifest within its max-age=300 window.
Common situations: CloudFront minimum TTL set above 300 seconds pinning stale current.json; a distribution misconfiguration serving 403/404 for the prefix; wrong PAPERCLIP_PAGE_BASE_URL so verification hits a different host than was uploaded to; S3 upload to a different bucket than the CDN origin.
Understand the failure class
Background: Request timed out: what client-side request timeouts mean across libraries (Request timed out, TIMED_OUT, APITimeoutError) — this error's family across 39 libraries.
Related errors
- Public announcement asset headers are not ready
- --agent-name cannot be empty.
- --api-key and --api-key-env are mutually exclusive.
- Artifact download failed: HTTP
- attachment_not_ready
AI-assisted analysis of paperclipai/paperclip@3f1d897a7c (2026-09-18).
Data as JSON: /api/errors/58c2cf1803c344f1.
Report an issue: GitHub.
Appendix: source
Thrown at scripts/publish-announcements.ts:125
const body = announcementManifestSchema.parse(await response.json());
if (response.ok && JSON.stringify(body) === JSON.stringify(prepared.manifest)
&& /(?:^|,)\s*max-age=300(?:\s*,|$)/i.test(response.headers.get("cache-control") ?? "")) {
for (const asset of prepared.files.slice(0, -1)) {
const image = await fetch(`${baseUrl}/${asset.key}`, { method: "HEAD", signal: AbortSignal.timeout(10_000), credentials: "omit", redirect: "error" });
const caching = image.headers.get("cache-control") ?? "";
if (!image.ok || image.headers.get("content-type") !== asset.contentType
|| !/(?:^|,)\s*max-age=31536000(?:\s*,|$)/i.test(caching)
|| !/(?:^|,)\s*immutable(?:\s*,|$)/i.test(caching)) {
throw new Error("Public announcement asset headers are not ready");
}
}
console.log(`Published and verified: ${url}`);
return;
}
} catch { /* Retry edge propagation; uploads have already completed. */ }
if (attempt < 22) await new Promise((resolve) => setTimeout(resolve, 15_000));
}
throw new Error(`Uploaded, but public verification did not finish. Check ${url} and the CloudFront cache policy (minimum TTL must not exceed 300 seconds).`);
}
if (process.argv[1] && path.resolve(process.argv[1]) === fileURLToPath(import.meta.url)) {
main().catch((error) => {
console.error(error instanceof Error ? error.message : "Announcement publish failed");
process.exitCode = 1;
});
}
View on GitHub (pinned to 3f1d897a7c)