paperclipai/paperclip · error · Error
Set PAPERCLIP_PAGE_BUCKET before publishing
Error message
Set PAPERCLIP_PAGE_BUCKET before publishing
What it means
Actual S3 upload only happens when the script is run in publish mode (not dry-run), and it requires PAPERCLIP_PAGE_BUCKET to name the destination bucket. This error means publish mode was requested but the bucket variable is unset or empty.
Solutions
- export PAPERCLIP_PAGE_BUCKET=<your-bucket-name> before running the script
- Add PAPERCLIP_PAGE_BUCKET to your CI environment/secrets configuration
- Re-run with publish mode omitted if you only wanted a dry-run
- Verify with: printenv PAPERCLIP_PAGE_BUCKET
Example fix
// before node scripts/publish-announcements.ts --publish # no bucket set // after PAPERCLIP_PAGE_BUCKET=my-pages-bucket node scripts/publish-announcements.ts --publish
Defensive patterns
Strategy: validation
Validate before calling
if (publish && !process.env.PAPERCLIP_PAGE_BUCKET) throw new Error("PAPERCLIP_PAGE_BUCKET is required for publish mode"); Try / catch
try { await main(); } catch (e) { if (e.message.startsWith("Set PAPERCLIP_PAGE_BUCKET")) { /* export the bucket and retry */ } } Prevention
- Export PAPERCLIP_PAGE_BUCKET in your shell profile or CI secrets before publish runs
- Do a dry-run first; only add --publish once env vars are confirmed
- Check printenv PAPERCLIP_PAGE_BUCKET in the exact environment (CI job, container) that runs the script
- Keep publish runs in CI where env vars are declared explicitly
When it happens
Trigger: Running the script with the publish flag while PAPERCLIP_PAGE_BUCKET is not exported in the environment (CI job, shell without the export, wrong profile).
Common situations: Dry-run validation passes locally, then the publish run in CI lacks the env var because secrets weren't injected; switching machines or shells where the variable was defined only in a local .env.
Understand the failure class
Background: "environment variable is not set" and "Missing keys in environment" errors: what missing required env var messages mean and how to fix them — this error's family across 28 libraries.
Related errors
- database-contract-unmet
- Invalid public base URL
- local_stdio_missing_secret
- PAPERCLIP_OPENCODE_RUNTIME_DIR is required
- ACPX profile requires exact model ; received
AI-assisted analysis of paperclipai/paperclip@3f1d897a7c (2026-09-18).
Data as JSON: /api/errors/30d8e11971ab1fee.
Report an issue: GitHub.
Appendix: source
Thrown at scripts/publish-announcements.ts:85
}
export function announcementUploadArgs(bucket: string, file: Awaited<ReturnType<typeof prepareAnnouncementPublish>>["files"][number]) {
return ["s3api", "put-object", "--bucket", bucket, "--key", file.key, "--body", file.file,
"--content-type", file.contentType, "--cache-control", file.cacheControl];
}
async function main() {
const { sourceDirectory, staging, publish } = parseAnnouncementPublishArgs(process.argv.slice(2));
const hostPrefix = process.env.PAPERCLIP_PAGE_DEFAULT_PREFIX;
const prepared = await prepareAnnouncementPublish(sourceDirectory, staging, hostPrefix);
const bucket = process.env.PAPERCLIP_PAGE_BUCKET;
const baseUrl = process.env.PAPERCLIP_PAGE_BASE_URL?.replace(/\/+$/, "") ?? "https://pages.paperclip.ing";
const url = `${baseUrl}/${announcementPublishPrefix(staging, hostPrefix)}/current.json`;
const parsed = new URL(url);
if (parsed.protocol !== "https:" || parsed.username || parsed.password || parsed.search || parsed.hash) throw new Error("Invalid public base URL");
console.log(JSON.stringify({ mode: publish ? "publish" : "dry-run", target: staging ? `staging/${staging}` : "production", bucket: bucket ?? "(unset)", url, announcementId: prepared.manifest.announcement?.id ?? null, files: prepared.files }, null, 2));
if (!publish) return;
if (!bucket) throw new Error("Set PAPERCLIP_PAGE_BUCKET before publishing");
const env = { ...process.env };
const key = env.PAPERCLIP_PAGE_AWS_ACCESS_KEY_ID;
const secret = env.PAPERCLIP_PAGE_AWS_SECRET_ACCESS_KEY;
if (Boolean(key) !== Boolean(secret)) throw new Error("Set both namespaced page uploader credential variables");
if (key && secret) {
env.AWS_ACCESS_KEY_ID = key;
env.AWS_SECRET_ACCESS_KEY = secret;
delete env.AWS_SESSION_TOKEN;
if (env.PAPERCLIP_PAGE_AWS_SESSION_TOKEN) env.AWS_SESSION_TOKEN = env.PAPERCLIP_PAGE_AWS_SESSION_TOKEN;
} else if (env.PAPERCLIP_PAGE_AWS_PROFILE) {
delete env.AWS_ACCESS_KEY_ID;
delete env.AWS_SECRET_ACCESS_KEY;
delete env.AWS_SESSION_TOKEN;
env.AWS_PROFILE = env.PAPERCLIP_PAGE_AWS_PROFILE;
}
// Only validated files, assets before manifest; credentials are scoped to AWS.
for (const file of prepared.files) execFileSync("aws", announcementUploadArgs(bucket, file), { env, stdio: "pipe" });
console.log("Uploaded. Checking the public manifest (CDN propagation can take five minutes)…");View on GitHub (pinned to 3f1d897a7c)