paperclipai/paperclip · error · Error
Usage: publish-announcements.ts [directory] [--staging…
Error message
Usage: publish-announcements.ts [directory] [--staging name] [--dry-run | --publish]
What it means
parseAnnouncementPublishArgs throws its usage string when the --publish or --dry-run flag appears more than once, or in a conflicting combination. The mode variable may hold only one value; a duplicate or second mode flag is a usage error.
Solutions
- Pass exactly one mode flag: either --publish or --dry-run, not both.
- Audit shell scripts/aliases for duplicate mode flags being appended.
- Default behavior with no flag is a dry-run unless --publish is given; drop the redundant flag.
- Wrap the call in argument validation that dedupes flags before parsing.
Example fix
// before publish-announcements.ts announcements --dry-run --publish // after publish-announcements.ts announcements --publish
Defensive patterns
Strategy: validation
Validate before calling
const modeFlags = args.filter(a => a === '--publish' || a === '--dry-run');
if (modeFlags.length > 1) throw new Error('Pass at most one of --publish / --dry-run'); Type guard
null
Try / catch
try {
const parsed = parseAnnouncementPublishArgs(args);
} catch (e) {
if (String(e.message).startsWith('Usage:')) {
console.error(e.message + ' (exactly one of --publish or --dry-run)');
process.exit(1);
}
throw e;
} Prevention
- Pass only one mode flag per invocation.
- Review shell scripts that concatenate flag arrays for duplicates.
- Rely on the default (no flag = dry-run) when unsure.
- Lint CI command lines for duplicated flags.
When it happens
Trigger: Running the CLI with both `--publish` and `--dry-run`, or repeating the same flag (e.g. `--dry-run --dry-run`), so the second occurrence finds mode already set.
Common situations: Script templates concatenating flag arrays so the mode flag appears twice; users passing both flags thinking one overrides the other; shell aliases appending --dry-run while the user also passes --publish.
Understand the failure class
Background: "mutually exclusive" flag errors: what "can't supply both nx and xx", "--raw is not compatible with -i" and "cannot be used with" mean, and how to fix them — this error's family across 29 libraries.
Related errors
- requires a comma-separated value
- missing
- unknown argument
- Usage: pnpm smoke:local-provider -- --profile <
- A reusable lease cannot be replaced and reacquired in the…
AI-assisted analysis of paperclipai/paperclip@3f1d897a7c (2026-09-18).
Data as JSON: /api/errors/453ee83c9d4366bf.
Report an issue: GitHub.
Appendix: source
Thrown at scripts/publish-announcements.ts:27
import { validateAnnouncementAnimation } from "../server/src/services/announcement-animation.js";
export function announcementPublishPrefix(staging?: string, hostPrefix?: string) {
if (hostPrefix !== undefined && !/^[a-z0-9-]+(?:\/[a-z0-9-]+)*$/.test(hostPrefix)) {
throw new Error("Invalid PAPERCLIP_PAGE_DEFAULT_PREFIX: use lowercase path segments without leading or trailing slashes");
}
const prefix = staging === undefined ? "announcements/v1" : `announcements/staging/${announcementIdSchema.parse(staging)}/v1`;
return hostPrefix ? `${hostPrefix}/${prefix}` : prefix;
}
export function parseAnnouncementPublishArgs(args: string[]) {
let sourceDirectory: string | undefined;
let staging: string | undefined;
let mode: "publish" | "dry-run" | undefined;
const usage = "Usage: publish-announcements.ts [directory] [--staging name] [--dry-run | --publish]";
for (let index = 0; index < args.length; index++) {
const arg = args[index];
if (arg === "--publish" || arg === "--dry-run") {
if (mode) throw new Error(usage);
mode = arg === "--publish" ? "publish" : "dry-run";
} else if (arg === "--staging") {
if (staging !== undefined || !args[index + 1]) throw new Error(usage);
staging = announcementIdSchema.parse(args[++index]);
} else if (arg.startsWith("--") || sourceDirectory !== undefined) {
throw new Error(usage);
} else {
sourceDirectory = arg;
}
}
return { sourceDirectory: sourceDirectory ?? (staging ? "announcements/examples/staging" : "announcements"), staging, publish: mode === "publish" };
}
export async function prepareAnnouncementPublish(sourceDirectory: string, staging?: string, hostPrefix?: string) {
const prefix = announcementPublishPrefix(staging, hostPrefix);
const source = path.resolve(sourceDirectory);
if (!(await lstat(source)).isDirectory()) throw new Error("Source must be a real directory");
const manifestPath = path.join(source, "current.json");View on GitHub (pinned to 3f1d897a7c)