{"record":{"id":"d3f115463da68552","repo":"paperclipai/paperclip","slug":"file-is-required","errorCode":null,"errorMessage":"--file is required","messagePattern":"--file is required","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"cli/src/commands/client/asset.ts","lineNumber":95,"sourceCode":"            printOutput({ ok: true, out: opts.out, bytes: bytes.length }, { json: ctx.json });\n            return;\n          }\n          process.stdout.write(bytes);\n        } catch (err) {\n          handleCommandError(err);\n        }\n      }),\n  );\n}\n\nasync function uploadAsset(\n  apiBase: string,\n  apiKey: string | undefined,\n  path: string,\n  opts: AssetOptions,\n): Promise<unknown> {\n  if (!opts.file?.trim()) {\n    throw new Error(\"--file is required\");\n  }\n  const bytes = await readFile(opts.file);\n  const form = new FormData();\n  form.set(\"file\", new Blob([bytes], { type: inferContentTypeFromPath(opts.file) }), opts.file.split(/[\\\\/]/).pop() ?? \"asset\");\n  if (opts.namespace?.trim()) form.set(\"namespace\", opts.namespace.trim());\n  if (opts.alt?.trim()) form.set(\"alt\", opts.alt.trim());\n  if (opts.title?.trim()) form.set(\"title\", opts.title.trim());\n\n  const response = await fetch(buildApiUrl(apiBase, path), {\n    method: \"POST\",\n    headers: apiKey ? { authorization: `Bearer ${apiKey}` } : undefined,\n    body: form,\n  });\n  return parseFetchResponse(response);\n}\n\nasync function downloadAsset(apiBase: string, apiKey: string | undefined, assetId: string): Promise<Buffer> {\n  const response = await fetch(buildApiUrl(apiBase, apiPath`/api/assets/${assetId}/content`), {","sourceCodeStart":77,"sourceCodeEnd":113,"githubUrl":"https://github.com/paperclipai/paperclip/blob/67001ec6eb96ae601aa27bc91d9b2415d665334a/cli/src/commands/client/asset.ts#L77-L113","documentation":"Thrown by uploadAsset() in cli/src/commands/client/asset.ts when the --file option is missing, empty, or whitespace-only. The check happens before any filesystem or network access, so this is a pure usage guard for `paperclipai asset upload` (and any command reusing uploadAsset).","triggerScenarios":"Running `paperclipai asset upload` without --file, or with --file '' / --file '   '. The guard uses opts.file?.trim(), so even an all-whitespace path is rejected.","commonSituations":"Forgot the flag. Shell expansion produced an empty string (e.g. --file \"$MISSING_VAR”). Flag typo such as --filename.","solutions":["Pass an explicit path: `paperclipai asset upload --file ./logo.png`.","If using a variable, ensure it is set: `--file \"${ASSET_PATH:?missing}\"`.","Confirm the flag name is --file (not --filename/--path)."],"exampleFix":"// before\nASSET=\"\"; paperclipai asset upload --file \"$ASSET\"\n// after\nASSET=./logo.png; paperclipai asset upload --file \"$ASSET\"","handlingStrategy":"validation","validationCode":"function resolveAssetPath(file: string | undefined): string {\n  const p = file?.trim();\n  if (!p) throw new Error('--file is required (pass a path to the asset)');\n  return p;\n}","typeGuard":null,"tryCatchPattern":"try { await uploadAsset(apiBase, apiKey, path, opts); }\ncatch (err) {\n  if (err instanceof Error && err.message === '--file is required') {\n    console.error('Usage: paperclipai asset upload --file <path> [--namespace ns] [--alt ...] [--title ...]');\n    process.exit(2);\n  }\n  throw err;\n}","preventionTips":["Always pass --file explicitly to asset upload.","In shell wrappers, guard empty variables: --file \"${ASSET_PATH:?missing}\".","Confirm the flag name is --file before scripting."],"tags":["cli","asset","validation","usage"],"backgroundTag":null,"analyzedSha":"67001ec6eb96ae601aa27bc91d9b2415d665334a","analyzedAt":"2026-08-12T12:05:45.408Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}