{"record":{"id":"c77adc3ff6c41271","repo":"paperclipai/paperclip","slug":"that-file-is-empty-choose-the-private-key-downloaded-from","errorCode":null,"errorMessage":"That file is empty. Choose the private key downloaded from your GitHub App.","messagePattern":"That file is empty\\. Choose the private key downloaded from your GitHub App\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"ui/src/pages/apps/chat/github-private-key-file.ts","lineNumber":23,"sourceCode":"  return {\n    start() {\n      revision += 1;\n      return revision;\n    },\n    invalidate() {\n      revision += 1;\n    },\n    isCurrent(candidate: number) {\n      return candidate === revision;\n    },\n  };\n}\n\nexport async function readGitHubPrivateKeyFile(\n  file: Pick<File, \"size\" | \"text\">,\n): Promise<string> {\n  if (file.size === 0) {\n    throw new Error(\n      \"That file is empty. Choose the private key downloaded from your GitHub App.\",\n    );\n  }\n  if (file.size > GITHUB_PRIVATE_KEY_FILE_MAX_BYTES) {\n    throw new Error(\n      \"That file is too large. Choose a GitHub App private key smaller than 64 KB.\",\n    );\n  }\n\n  let value: string;\n  try {\n    value = await file.text();\n  } catch {\n    throw new Error(\n      \"Paperclip couldn't read that file. Choose the .pem file again or paste the private key.\",\n    );\n  }\n  if (!value.trim()) {","sourceCodeStart":5,"sourceCodeEnd":41,"githubUrl":"https://github.com/paperclipai/paperclip/blob/3f1d897a7c018d76563a21c6e39c3c9b03933622/ui/src/pages/apps/chat/github-private-key-file.ts#L5-L41","documentation":"readGitHubPrivateKeyFile validates the file the user selected for a GitHub App private key before reading it. A file whose `size` is exactly 0 cannot be a valid .pem key, so the function throws immediately. This check runs before file.text() so empty selections fail fast with actionable copy.","triggerScenarios":"User selects a 0-byte file in the GitHub App setup file input (e.g. an interrupted download or an empty placeholder file); a programmatically constructed File/Blob with size 0 is passed to `privateKey` -> readGitHubPrivateKeyFile.","commonSituations":"Browser download of the GitHub App .pem was interrupted leaving a zero-byte file; user picked the wrong (empty) file in the picker; automated test passes `new File([], \"key.pem\")` which has size 0.","solutions":["Re-download the private key from the GitHub App settings page (Developper settings > GitHub Apps > your app > Private key > Generate) and select the new .pem file.","Check the file in the picker handler and reject zero-byte files with a clear message before calling the reader.","If constructing a File in tests or code, include actual PEM content so size > 0.","Fall back to the paste-private-key textarea option instead of file upload."],"exampleFix":"// before\nconst [file] = e.target.files;\nawait readGitHubPrivateKeyFile(file);\n\n// after\nconst [file] = e.target.files;\nif (file.size === 0) {\n  setError(\"That file is empty. Re-download your GitHub App private key (.pem).\");\n  return;\n}\nawait readGitHubPrivateKeyFile(file);","handlingStrategy":"validation","validationCode":"if (file.size === 0) { showError(\"That file is empty. Choose the private key downloaded from your GitHub App.\"); return; }","typeGuard":"const isNonEmptyFile = (f: Pick<File, \"size\">): boolean => f.size > 0;","tryCatchPattern":"try {\n  const key = await readGitHubPrivateKeyFile(file);\n} catch (err) {\n  if (err instanceof Error && err.message.includes(\"empty\")) {\n    showError(err.message);\n  } else { throw err; }\n}","preventionTips":["Check file.size before reading in the input onChange handler","Re-download keys rather than keeping old copies","Surface the paste-key option alongside file upload","Validate selected file starts with '-----BEGIN' for early feedback"],"tags":["file-upload","validation","github-app","empty-file"],"backgroundTag":"empty-required-field","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"}