paperclipai/paperclip · error · GitHubAttachmentUnavailableError

github_attachment_canonical_html_unavailable

github_attachment_canonical_html_unavailable

Error message

github_attachment_canonical_html_unavailable

What it means

GitHubAttachmentUnavailableError with code github_attachment_canonical_html_unavailable is thrown by resolveCanonicalAttachmentTargetOrThrow in chat-github-attachments.ts when the GitHub API comment/issue response row's body_html field is not a string or exceeds 600,000 characters. The service relies on GitHub's server-rendered HTML to locate the signed image anchor; without usable body_html it cannot safely resolve a canonical download target. This is a deliberate guard, not a network failure.

Source

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

  if (
    String(row.id) !== locator.sourceMessageId ||
    typeof row.url !== "string" ||
    row.url.toLowerCase() !== request.url.toLowerCase()
  )
    throw new GitHubAttachmentUnavailableError(
      "github_attachment_canonical_source_mismatch",
    );
  if (
    typeof row.body !== "string" ||
    row.body.length > 200_000 ||
    createHash("sha256").update(row.body).digest("hex") !==
      locator.sourceBodySha256
  )
    throw new GitHubAttachmentUnavailableError(
      "github_attachment_canonical_body_mismatch",
    );
  if (typeof row.body_html !== "string" || row.body_html.length > 600_000)
    throw new GitHubAttachmentUnavailableError(
      "github_attachment_canonical_html_unavailable",
    );
  if (thread[4]) {
    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",
    );

View on GitHub (pinned to 01ad858492)

Solutions

  1. Ensure the comment resolver fetches the GitHub API row with rendered HTML included (default JSON representation returns body_html; do not pass Accept headers that strip it).
  2. Verify the resolver passes the raw API row object into resolveGitHubCommentAttachmentTarget, not a projection that drops body_html.
  3. If the comment is genuinely huge (>600KB HTML), reduce the comment content on GitHub or split the attachment into a smaller comment; the cap is intentional.
  4. Check fixtures/tests supply a realistic body_html string.

Example fix

// before
resolveGitHubCommentAttachmentTarget(attachment, { id: row.id, body: row.body });
// after
resolveGitHubCommentAttachmentTarget(attachment, row); // keep full API row incl. body_html
Defensive patterns

Strategy: validation

Validate before calling

function hasRenderableHtml(row: unknown): boolean {
  return typeof row === 'object' && row !== null && 'body_html' in row &&
    typeof (row as any).body_html === 'string' && (row as any).body_html.length <= 600_000;
}
if (!hasRenderableHtml(apiRow)) throw new Error('resolver response lacks usable body_html');

Type guard

const isRowWithHtml = (v: unknown): v is { body_html: string; [k: string]: unknown } =>
  typeof v === 'object' && v !== null && typeof (v as any).body_html === 'string';

Try / catch

try {
  const target = resolveGitHubCommentAttachmentTarget(attachment, row);
  if (target === null) skipCanonicalPath(attachment);
} catch (e) { if (e instanceof GitHubAttachmentUnavailableError) fallback(attachment); else throw e; }

Prevention

When it happens

Trigger: Calling resolveGitHubCommentAttachmentTarget/prepareGitHubPublicAttachment for a GitHub comment whose API response has body_html missing or null (e.g. mime_type requested as text only), or a comment whose rendered HTML exceeds the 600KB cap (very large comment bodies).

Common situations: Resolver responses built from an API client configured without 'application/vnd.github.html+json' or without full JSON rendering; extremely long issue comments with many embedded images; mocked/stubbed GitHub API fixtures that omit body_html; GitHub changing the render payload shape.

Understand the failure class

Background: "invalid response format", "malformed payload", "missing data field": when an API returns 200 but the response shape is wrong — this error's family across 23 libraries.

Related errors


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