{"record":{"id":"7a05668c906af7d5","repo":"paperclipai/paperclip","slug":"github-attachment-canonical-image-without-source-a","errorCode":"github_attachment_canonical_image_without_source_anchor","errorMessage":"github_attachment_canonical_image_without_source_anchor","messagePattern":"github_attachment_canonical_image_without_source_anchor","errorType":"error_code","errorClass":"GitHubAttachmentUnavailableError","httpStatus":null,"severity":"error","filePath":"server/src/services/chat-github-attachments.ts","lineNumber":665,"sourceCode":"    candidates.push(target);\n  }\n  const sameAssetImages = [...fragment.querySelectorAll(\"img[src]\")].filter(\n    (image) =>\n      image.getAttribute(\"src\") === locator.url ||\n      sameAssetImage(image.getAttribute(\"src\")!),\n  );\n  if (candidates.length > 1 || sameAssetImages.length > 1)\n    throw new GitHubAttachmentUnavailableError(\n      \"github_attachment_canonical_mapping_ambiguous\",\n    );\n  if (candidates.length === 1 && sameAssetImages.length === 1)\n    return candidates[0]!;\n  if (\n    [...fragment.querySelectorAll(\"img[src]\")].some((image) =>\n      signedImage(image.getAttribute(\"src\")!),\n    )\n  )\n    throw new GitHubAttachmentUnavailableError(\n      \"github_attachment_canonical_image_without_source_anchor\",\n    );\n  throw new GitHubAttachmentUnavailableError(\n    \"github_attachment_canonical_anchor_missing\",\n  );\n}\n\nfunction imageSignatureMatches(body: Buffer, mime: string): boolean {\n  if (mime === \"image/png\")\n    return body\n      .subarray(0, 8)\n      .equals(Buffer.from([137, 80, 78, 71, 13, 10, 26, 10]));\n  if (mime === \"image/jpeg\" || mime === \"image/jpg\")\n    return body[0] === 255 && body[1] === 216 && body[2] === 255;\n  if (mime === \"image/gif\")\n    return /^(GIF87a|GIF89a)$/.test(body.subarray(0, 6).toString(\"ascii\"));\n  if (mime === \"image/webp\")\n    return (","sourceCodeStart":647,"sourceCodeEnd":683,"githubUrl":"https://github.com/paperclipai/paperclip/blob/01ad8584922b5d85292b1723cae71fa0d9b07a19/server/src/services/chat-github-attachments.ts#L647-L683","documentation":"GitHubAttachmentUnavailableError with code github_attachment_canonical_image_without_source_anchor is thrown when no valid candidate anchor was found, but the HTML does contain at least one img whose src is a valid signed same-asset image. That means the signed image exists in the rendering but is not wrapped by an anchor matching the original locator URL — the expected anchor-to-image mapping is broken.","triggerScenarios":"The comment's rendered HTML contains a bare signed <img> (not inside an <a href> matching the locator URL), e.g. markdown that inlines the signed URL directly instead of the original /assets/ link, or the original anchor was edited away while the signed img remains.","commonSituations":"Users replacing the markdown link form [![img](assets-url)](assets-url) with a direct signed-URL image; GitHub caching mixing old/new render forms; comment edits removing the anchor while keeping the image.","solutions":["Restore the standard GitHub image-anchor form in the comment: anchor href = original /assets/ URL wrapping the image.","Re-record the attachment locator against the comment's current body/SHA-256 after any edit.","If the signed URL was pasted manually, replace it with the original github.com/user-attachments/assets/<id> URL."],"exampleFix":"// before\n<img src=\"https://private-user-images.../1-abc.png?jwt=...\">\n// after\n<a href=\"https://github.com/user-attachments/assets/abc\"><img src=\"https://private-user-images.../1-abc.png?jwt=...\"></a>","handlingStrategy":"try-catch","validationCode":"// markdown-level pre-check: image should appear in GitHub anchor form [![..](assets-url)](assets-url)\nconst anchorForm = new RegExp(`\\\\[!\\\\[[^\\\\]]*\\\\]\\\\(${escapedAssetUrl}\\\\)\\\\]\\\\(${escapedAssetUrl}\\\\)`);\nif (!anchorForm.test(commentBody)) console.warn('image not in standard anchor form');","typeGuard":null,"tryCatchPattern":"const target = resolveGitHubCommentAttachmentTarget(attachment, row);\nif (target === null) {\n  // bare signed img without source anchor: re-record locator against current body\n  await rederiveLocator(attachment);\n}","preventionTips":["Keep GitHub's default anchor rendering; don't hand-edit to bare signed URLs.","Re-record the locator after any comment edit.","Preserve the original /assets/ URL as the anchor href.","Detect and normalize manually pasted signed URLs at ingestion."],"tags":["github","attachments","html-parsing","anchors"],"backgroundTag":"unexpected-response-shape","analyzedSha":"01ad8584922b5d85292b1723cae71fa0d9b07a19","analyzedAt":"2026-09-10T03:14:50.855Z","contentChangedAt":"2026-09-10T03:14:50.855Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}