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
- Ensure the attachment went through the GitHub comment attachment registration path so a locator exists in `handles` before resolving.
- 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.
- 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
- Only call resolveGitHubCommentAttachmentTarget on attachments that went through the GitHub comment registration path.
- Always pass the parsed single-comment JSON object, never an array or an envelope like { comment: ... }.
- Await the fetch/registration before resolution; do not resolve concurrently with registration.
- When consuming response.json(), add a quick object-shape check before handing the value to the resolver.
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
- CHAT_PROVIDER_PRETRANSPORT_REJECTED
- github_attachment_source_mismatch
- Invalid status '${String(rawStatus)}'. Must be one of: ${PLU
- "tool" is required and must be a string
- "runContext" is required and must be an object
AI-assisted analysis of paperclipai/paperclip@01ad858492 (2026-09-10).
Data as JSON: /api/errors/3ed963f082d2fd86.
Report an issue: GitHub.