paperclipai/paperclip · error · GitHubAttachmentUnavailableError

github_attachment_canonical_response_unavailable

github_attachment_canonical_response_unavailable

Error message

github_attachment_canonical_response_unavailable

What it means

resolveCanonicalAttachmentTargetOrThrow requires that the attachment be registered with a source locator and a canonical comment request, and that the caller pass a non-null, non-array JSON object as the canonical API response. If the internal handle/locator is missing (the attachment was not registered as a GitHub comment attachment) or the supplied value is not an object, it throws github_attachment_canonical_response_unavailable.

Source

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

    if (error instanceof GitHubAttachmentUnavailableError) return null;
    throw error;
  }
}

function resolveCanonicalAttachmentTargetOrThrow(
  attachment: Attachment,
  value: unknown,
): URL {
  const locator = handles.get(attachment);
  const request = githubAttachmentCommentRequest(attachment);
  if (
    !locator ||
    !request ||
    !value ||
    typeof value !== "object" ||
    Array.isArray(value)
  )
    throw new GitHubAttachmentUnavailableError(
      "github_attachment_canonical_response_unavailable",
    );
  const row = value as Record<string, unknown>;
  const thread =
    /^github:([^:]+):(?:(issue):)?([1-9][0-9]*)(?::rc:([1-9][0-9]*))?$/i.exec(
      locator.sourceThreadId,
    )!;
  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 ||

View on GitHub (pinned to 01ad858492)

Solutions

  1. Ensure the attachment went through the GitHub comment attachment registration path so a locator exists in `handles` before resolving.
  2. Pass the single canonical comment JSON object (the parsed body of GET api.github.com/repos/{owner}/{repo}/issues/comments/{id}), not an array or wrapped envelope.
  3. Only invoke resolveGitHubCommentAttachmentTarget for attachments created from GitHub comment sources; for other attachment kinds use the appropriate resolver (it returns null rather than throwing).

Example fix

// before
const target = resolveGitHubCommentAttachmentTarget(attachment, comments); // array
// after
const comment = Array.isArray(comments) ? comments[0] : comments;
const target = resolveGitHubCommentAttachmentTarget(attachment, comment);
Defensive patterns

Strategy: type-guard

Validate before calling

// Before resolving, confirm registration and shape
const req = githubAttachmentCommentRequest(attachment);
if (!req) throw new Error('attachment is not a GitHub comment attachment');
if (value == null || typeof value !== 'object' || Array.isArray(value))
  throw new Error('canonical response must be a JSON object');

Type guard

function isCanonicalCommentObject(v: unknown): v is Record<string, unknown> {
  return v !== null && typeof v === 'object' && !Array.isArray(v);
}

Try / catch

const target = resolveGitHubCommentAttachmentTarget(attachment, value);
if (target === null) {
  // Includes github_attachment_canonical_response_unavailable; treat as unresolvable, not a crash
  return renderFallback(attachment);
}

Prevention

When it happens

Trigger: resolveGitHubCommentAttachmentTarget is called with an attachment that has no entry in the internal `handles` map (never registered as a GitHub comment attachment); githubAttachmentCommentRequest(attachment) returns null; or the canonical API response `value` is null, undefined, an array, or a primitive instead of a JSON object.

Common situations: Passing the raw parsed JSON array of comments instead of the single comment object; calling resolution on a non-comment attachment type (e.g. a direct file attachment); calling before the fetch/registration step completed; a refactor changed the value passed from `response.json()` to something nested.

Related errors


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