paperclipai/paperclip · error · Error

Paperclip couldn't read that file. Choose the .pem file…

Error message

Paperclip couldn't read that file. Choose the .pem file again or paste the private key.

What it means

When `file.text()` rejects (the browser cannot decode/read the selected file), readGitHubPrivateKeyFile converts the underlying read error into this user-facing message. It indicates the File handle is unreadable — not that the key is invalid — so it advises re-selecting the file or pasting the key.

Solutions

  1. Re-select the .pem file in the picker and submit again without moving/deleting it in between.
  2. Open the file in a text editor to verify it is plain-text PEM; if it is binary, re-download the key from GitHub App settings.
  3. Use the paste-private-key textarea as the fallback path.
  4. Handle the rejection in the caller and show this message plus the paste option rather than a raw DOMException.

Example fix

// before
value = await file.text();

// after
try {
  value = await file.text();
} catch (err) {
  setError("Paperclip couldn't read that file. Choose the .pem file again or paste the private key.");
  return;
}
Defensive patterns

Strategy: fallback

Validate before calling

// probe readability first
try { await file.slice(0, 8).text(); } catch { showPasteFallback(); return; }

Type guard

const isReadableFile = (f: Pick<File, "text">): f is File & { text(): Promise<string> } => typeof f.text === "function";

Try / catch

try {
  const key = await readGitHubPrivateKeyFile(file);
} catch (err) {
  if (err instanceof Error && err.message.includes("couldn't read")) {
    openPasteKeyTextarea();
  } else { throw err; }
}

Prevention

When it happens

Trigger: file.text() throws because the file was deleted/moved between selection and read; permission issues on the picked file; the browser fails to decode the file as text (binary/UTF-16 content); FileSystemFileHandle backed file is stale after a directory change.

Common situations: User selected the key, then deleted or re-downloaded it before submitting; picked a binary file (zip, image) that cannot be read as UTF-8 text; browser storage quota/handle invalidation after the picker returned a stale entry.

Understand the failure class

Background: "failed to read file", EACCES, ENOENT and "could not read <path>" errors: when a program can't read a file from disk — this error's family across 49 libraries.

Related errors


AI-assisted analysis of paperclipai/paperclip@3f1d897a7c (2026-09-18). Data as JSON: /api/errors/6b2062477059b3fc. Report an issue: GitHub.

Appendix: source

Thrown at ui/src/pages/apps/chat/github-private-key-file.ts:37

export async function readGitHubPrivateKeyFile(
  file: Pick<File, "size" | "text">,
): Promise<string> {
  if (file.size === 0) {
    throw new Error(
      "That file is empty. Choose the private key downloaded from your GitHub App.",
    );
  }
  if (file.size > GITHUB_PRIVATE_KEY_FILE_MAX_BYTES) {
    throw new Error(
      "That file is too large. Choose a GitHub App private key smaller than 64 KB.",
    );
  }

  let value: string;
  try {
    value = await file.text();
  } catch {
    throw new Error(
      "Paperclip couldn't read that file. Choose the .pem file again or paste the private key.",
    );
  }
  if (!value.trim()) {
    throw new Error(
      "That file is empty. Choose the private key downloaded from your GitHub App.",
    );
  }
  return value;
}

View on GitHub (pinned to 3f1d897a7c)