{"record":{"id":"a3225419ee7dcab1","repo":"paperclipai/paperclip","slug":"invalid-or-oversized-current-json","errorCode":null,"errorMessage":"Invalid or oversized current.json","messagePattern":"Invalid or oversized current\\.json","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"scripts/publish-announcements.ts","lineNumber":47,"sourceCode":"    } else if (arg === \"--staging\") {\n      if (staging !== undefined || !args[index + 1]) throw new Error(usage);\n      staging = announcementIdSchema.parse(args[++index]);\n    } else if (arg.startsWith(\"--\") || sourceDirectory !== undefined) {\n      throw new Error(usage);\n    } else {\n      sourceDirectory = arg;\n    }\n  }\n  return { sourceDirectory: sourceDirectory ?? (staging ? \"announcements/examples/staging\" : \"announcements\"), staging, publish: mode === \"publish\" };\n}\n\nexport async function prepareAnnouncementPublish(sourceDirectory: string, staging?: string, hostPrefix?: string) {\n  const prefix = announcementPublishPrefix(staging, hostPrefix);\n  const source = path.resolve(sourceDirectory);\n  if (!(await lstat(source)).isDirectory()) throw new Error(\"Source must be a real directory\");\n  const manifestPath = path.join(source, \"current.json\");\n  const stat = await lstat(manifestPath);\n  if (!stat.isFile() || stat.size > ANNOUNCEMENT_MANIFEST_MAX_BYTES) throw new Error(\"Invalid or oversized current.json\");\n  const manifest = announcementManifestSchema.parse(JSON.parse(await readFile(manifestPath, \"utf8\")));\n  const files: Array<{ file: string; key: string; contentType: string; cacheControl: string }> = [];\n  for (const kind of [\"image\", \"animation\"] as const) {\n    const asset = manifest.announcement?.[kind];\n    if (!asset) continue;\n    if (!(await lstat(path.join(source, \"assets\"))).isDirectory()) throw new Error(\"Assets must be a real directory\");\n    const assetPath = asset.path;\n    const file = path.join(source, assetPath);\n    const assetStat = await lstat(file);\n    const maximum = kind === \"animation\" ? ANNOUNCEMENT_ANIMATION_MAX_BYTES : ANNOUNCEMENT_IMAGE_MAX_BYTES;\n    if (!assetStat.isFile() || assetStat.size > maximum) throw new Error(`Invalid or oversized ${kind}`);\n    const bytes = await readFile(file);\n    const digest = createHash(\"sha256\").update(bytes).digest(\"hex\");\n    if (!assetPath.startsWith(`assets/${digest}.`)) throw new Error(\"Asset filename must match its SHA-256 digest\");\n    if (kind === \"animation\") validateAnnouncementAnimation(bytes);\n    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\" });\n  }\n  files.push({ file: manifestPath, key: `${prefix}/current.json`, contentType: \"application/json\", cacheControl: \"public,max-age=300\" });","sourceCodeStart":29,"sourceCodeEnd":65,"githubUrl":"https://github.com/paperclipai/paperclip/blob/3f1d897a7c018d76563a21c6e39c3c9b03933622/scripts/publish-announcements.ts#L29-L65","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before: oversized manifest with inlined base64\n{ \"announcement\": { \"image\": { \"data\": \"iVBORw0KGgo...\" } } }\n// after: reference the asset file instead\n{ \"announcement\": { \"image\": { \"file\": \"assets/banner.png\" } } }","handlingStrategy":"validation","validationCode":"import { statSync } from 'node:fs';\nconst m = path.join(source, 'current.json');\nconst s = statSync(m, { throwIfNoEntry: false });\nif (!s?.isFile() || s.size > ANNOUNCEMENT_MANIFEST_MAX_BYTES) {\n  throw new Error(`current.json missing, not a file, or larger than ${ANNOUNCEMENT_MANIFEST_MAX_BYTES} bytes`);\n}\nJSON.parse(readFileSync(m, 'utf8'));","typeGuard":"const validManifest = async (dir) => {\n  try {\n    const st = await lstat(path.join(dir, 'current.json'));\n    return st.isFile() && st.size <= ANNOUNCEMENT_MANIFEST_MAX_BYTES;\n  } catch { return false; }\n};","tryCatchPattern":"try {\n  await prepareAnnouncementPublish(dir, staging, prefix);\n} catch (e) {\n  if (String(e.message).includes('Invalid or oversized current.json')) {\n    console.error('Regenerate current.json and keep it under ANNOUNCEMENT_MANIFEST_MAX_BYTES.');\n    process.exit(1);\n  }\n  throw e;\n}","preventionTips":["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."],"tags":["filesystem","validation","file-size","manifest"],"backgroundTag":"file-size-limit-exceeded","analyzedSha":"3f1d897a7c018d76563a21c6e39c3c9b03933622","analyzedAt":"2026-09-18T08:03:59.046Z","contentChangedAt":"2026-09-18T08:03:59.046Z","schemaVersion":2},"datasetVersion":"2026-09-22T06:17:15.046Z"}