paperclipai/paperclip · warning · GitHubAttachmentUnavailableError

github_attachment_canonical_file_unsupported

github_attachment_canonical_file_unsupported

Error message

github_attachment_canonical_file_unsupported

What it means

GitHubAttachmentUnavailableError with code github_attachment_canonical_file_unsupported is thrown when the attachment locator URL does not end in /assets/<uuid>, i.e. it is not a GitHub user-upload image asset. Only image assets on GitHub's asset hosts have a documented signed-download representation; generic private files (e.g. repository files or other upload kinds) deliberately cannot be canonicalized to a signed URL.

Source

Thrown at server/src/services/chat-github-attachments.ts:594

    if (
      row.pull_request_url !==
        `https://api.github.com/repos/${thread[1]}/pulls/${thread[3]}` ||
      String(row.in_reply_to_id ?? row.id) !== thread[4]
    )
      throw new GitHubAttachmentUnavailableError(
        "github_attachment_canonical_source_mismatch",
      );
  } else if (
    row.issue_url !==
    `https://api.github.com/repos/${thread[1]}/issues/${thread[3]}`
  )
    throw new GitHubAttachmentUnavailableError(
      "github_attachment_canonical_source_mismatch",
    );
  const assetId = /\/assets\/([a-f0-9-]+)$/i.exec(locator.url)?.[1];
  // Generic private files have no documented signed-download representation.
  if (!assetId)
    throw new GitHubAttachmentUnavailableError(
      "github_attachment_canonical_file_unsupported",
    );
  const sourceBody = row.body;
  const imagePath = new RegExp(
    `^/[1-9][0-9]*/[1-9][0-9]*-${assetId}\\.(?:png|jpe?g|gif|webp)$`,
    "i",
  );
  const sameAssetImage = (src: string): URL | null => {
    const target = allowedRedirect(src, locator.url);
    return target?.hostname === "private-user-images.githubusercontent.com" &&
      imagePath.test(target.pathname)
      ? target
      : null;
  };
  const signedImage = (src: string): URL | null => {
    const target = sameAssetImage(src);
    return target &&
      [...target.searchParams.keys()].join(",") === "jwt" &&

View on GitHub (pinned to 01ad858492)

Solutions

  1. Only run canonical resolution for image attachments whose URL ends in /assets/<uuid> (png/jpg/gif/webp).
  2. For non-image or generic files, serve them through the direct download path instead of signed-URL canonicalization.
  3. Check how the attachment handle was created; fix upstream URL extraction to preserve the /assets/<uuid> form.

Example fix

// before
resolveGitHubCommentAttachmentTarget(fileAttachment, row);
// after
if (/\/assets\/[a-f0-9-]+$/i.test(attachment.url)) resolveGitHubCommentAttachmentTarget(attachment, row);
Defensive patterns

Strategy: type-guard

Validate before calling

const isImageAsset = (url: string) => /\/assets\/[a-f0-9-]+\.(png|jpe?g|gif|webp)?/i.test(url) || /\/assets\/[a-f0-9-]+$/i.test(url);
if (!isImageAsset(attachment.url)) useDirectDownloadPath(attachment);

Type guard

const isCanonicalizable = (a: Attachment): boolean => /\/assets\/[a-f0-9-]+$/i.test(a.url);

Try / catch

try {
  return await prepareGitHubPublicAttachment(attachment);
} catch (e) {
  if (e instanceof GitHubAttachmentUnavailableError && e.code === 'github_attachment_canonical_file_unsupported')
    return directDownload(attachment);
  throw e;
}

Prevention

When it happens

Trigger: Calling resolveGitHubCommentAttachmentTarget for an attachment whose locator.url lacks a trailing /assets/<hex-uuid> segment — e.g. github user-attachments URLs of a non-asset form, or arbitrary file links pasted in a comment.

Common situations: Attachments created from generic file URLs (PDFs, zips, non-image user-attachments); URLs copied from rendered markdown where GitHub rewrites asset links; attempting canonical resolution for attachment kinds the feature never supported.

Understand the failure class

Background: UnsupportedOperationException and "is not supported" errors: when a library deliberately refuses a call — this error's family across 30 libraries.

Related errors


AI-assisted analysis of paperclipai/paperclip@01ad858492 (2026-09-10). Data as JSON: /api/errors/12b6fab7e7939aeb. Report an issue: GitHub.