paperclipai/paperclip · error · Error
Invalid or oversized current.json
Error message
Invalid or oversized current.json
What it means
prepareAnnouncementPublish reads <source>/current.json and requires it to be a regular file no larger than ANNOUNCEMENT_MANIFEST_MAX_BYTES. If the path is missing, is not a file, or exceeds the size cap, it throws 'Invalid or oversized current.json' before parsing the manifest.
Solutions
- Create a valid current.json in the source directory (or re-run the manifest generation step).
- Check the file size against ANNOUNCEMENT_MANIFEST_MAX_BYTES and trim/minify the JSON or move large assets to the assets/ folder referenced by path.
- Ensure current.json is a regular file, not a directory or symlink to something odd.
- Validate the JSON parses and matches announcementManifestSchema before publishing.
Example fix
// before: oversized manifest with inlined base64
{ "announcement": { "image": { "data": "iVBORw0KGgo..." } } }
// after: reference the asset file instead
{ "announcement": { "image": { "file": "assets/banner.png" } } } Defensive patterns
Strategy: validation
Validate before calling
import { statSync } from 'node:fs';
const m = path.join(source, 'current.json');
const s = statSync(m, { throwIfNoEntry: false });
if (!s?.isFile() || s.size > ANNOUNCEMENT_MANIFEST_MAX_BYTES) {
throw new Error(`current.json missing, not a file, or larger than ${ANNOUNCEMENT_MANIFEST_MAX_BYTES} bytes`);
}
JSON.parse(readFileSync(m, 'utf8')); Type guard
const validManifest = async (dir) => {
try {
const st = await lstat(path.join(dir, 'current.json'));
return st.isFile() && st.size <= ANNOUNCEMENT_MANIFEST_MAX_BYTES;
} catch { return false; }
}; Try / catch
try {
await prepareAnnouncementPublish(dir, staging, prefix);
} catch (e) {
if (String(e.message).includes('Invalid or oversized current.json')) {
console.error('Regenerate current.json and keep it under ANNOUNCEMENT_MANIFEST_MAX_BYTES.');
process.exit(1);
}
throw e;
} Prevention
- Never inline large binary/base64 content into current.json; reference files under assets/.
- Validate the manifest against announcementManifestSchema in a pre-publish check.
- Minify the JSON and avoid embedding history in the manifest.
- Re-run the manifest generation step if current.json is missing or a directory.
When it happens
Trigger: current.json missing from the source directory; current.json being a directory or symlink to a file; the manifest exceeding ANNOUNCEMENT_MANIFEST_MAX_BYTES because embedded content was inlined or it accumulated history.
Common situations: Generating the manifest with a tool that wrote it elsewhere; hand-editing the manifest and accidentally ballooning its size (pasted base64 images); a failed generation run leaving a truncated or directory-named current.json.
Understand the failure class
Background: "File too large" / "file size exceeds limit" errors: why libraries cap file sizes and how to fix them — this error's family across 46 libraries.
Related errors
- Assets must be a real directory
- Attempt source is not a real directory
- codex auth cache: account_id is not a valid account handle
- Codex working directory cannot be a filesystem root
- Codex working directory must exist before provider admission
AI-assisted analysis of paperclipai/paperclip@3f1d897a7c (2026-09-18).
Data as JSON: /api/errors/a3225419ee7dcab1.
Report an issue: GitHub.
Appendix: source
Thrown at scripts/publish-announcements.ts:47
} 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");
const stat = await lstat(manifestPath);
if (!stat.isFile() || stat.size > ANNOUNCEMENT_MANIFEST_MAX_BYTES) throw new Error("Invalid or oversized current.json");
const manifest = announcementManifestSchema.parse(JSON.parse(await readFile(manifestPath, "utf8")));
const files: Array<{ file: string; key: string; contentType: string; cacheControl: string }> = [];
for (const kind of ["image", "animation"] as const) {
const asset = manifest.announcement?.[kind];
if (!asset) continue;
if (!(await lstat(path.join(source, "assets"))).isDirectory()) throw new Error("Assets must be a real directory");
const assetPath = asset.path;
const file = path.join(source, assetPath);
const assetStat = await lstat(file);
const maximum = kind === "animation" ? ANNOUNCEMENT_ANIMATION_MAX_BYTES : ANNOUNCEMENT_IMAGE_MAX_BYTES;
if (!assetStat.isFile() || assetStat.size > maximum) throw new Error(`Invalid or oversized ${kind}`);
const bytes = await readFile(file);
const digest = createHash("sha256").update(bytes).digest("hex");
if (!assetPath.startsWith(`assets/${digest}.`)) throw new Error("Asset filename must match its SHA-256 digest");
if (kind === "animation") validateAnnouncementAnimation(bytes);
files.push({ file, key: `${prefix}/${assetPath}`, contentType: kind === "animation" ? "text/html" : assetPath.endsWith(".png") ? "image/png" : assetPath.endsWith(".jpg") ? "image/jpeg" : "image/webp", cacheControl: "public,max-age=31536000,immutable" });
}
files.push({ file: manifestPath, key: `${prefix}/current.json`, contentType: "application/json", cacheControl: "public,max-age=300" });View on GitHub (pinned to 3f1d897a7c)