{"record":{"id":"37bfd88025077bf2","repo":"paperclipai/paperclip","slug":"teams-inline-image-download-unavailable","errorCode":null,"errorMessage":"Teams inline image download unavailable","messagePattern":"Teams inline image download unavailable","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"server/src/services/chat-sdk-runtime.ts","lineNumber":2825,"sourceCode":"      throw Object.assign(\n        new Error(\"Telegram private response runtime unavailable\"),\n        { code: \"CHAT_PROVIDER_PRETRANSPORT_REJECTED\" },\n      );\n    }\n    return sendTelegramCallbackNotice(\n      { receipt, text, botToken: this.telegramEphemeralBotToken },\n      fetchImpl,\n    );\n  }\n\n  /** Only attachments reconstructed by this runtime can use the batch budget. */\n  async fetchTeamsInlineImage(\n    attachment: Attachment,\n    signal: AbortSignal,\n  ): Promise<Buffer> {\n    const fetcher = this.teamsInlineImageFetchers.get(attachment);\n    if (!fetcher || signal.aborted)\n      throw new Error(\"Teams inline image download unavailable\");\n    return await fetcher(signal);\n  }\n\n  /** Called only after current inbound admission; installation App authority only. */\n  async resolveGitHubAttachmentComment(\n    request: GitHubAttachmentCommentRequest,\n    signal: AbortSignal,\n  ): Promise<unknown> {\n    if (!this.githubAttachmentAppAuthority)\n      throw new GitHubAttachmentUnavailableError(\n        \"github_attachment_canonical_authority_unavailable\",\n      );\n    if (!isGitHubAttachmentCommentRequest(request))\n      throw new GitHubAttachmentUnavailableError(\n        \"github_attachment_source_mismatch\",\n      );\n    try {\n      signal.throwIfAborted();","sourceCodeStart":2807,"sourceCodeEnd":2843,"githubUrl":"https://github.com/paperclipai/paperclip/blob/01ad8584922b5d85292b1723cae71fa0d9b07a19/server/src/services/chat-sdk-runtime.ts#L2807-L2843","documentation":"fetchTeamsInlineImage downloads a Teams message inline image via a per-attachment fetcher that was registered when the attachment was admitted. If no fetcher exists for the given attachment, or the AbortSignal is already aborted, it throws 'Teams inline image download unavailable'. The download capability is intentionally ephemeral: fetchers exist only for attachments seen during inbound admission.","triggerScenarios":"Fetching an attachment that was never registered in this.teamsInlineImageFetchers (e.g. attachment data reconstructed or deserialized after admission, so the WeakMap/get lookup misses), or calling with an already-aborted AbortSignal.","commonSituations":"Re-hydrating an attachment object from DB/JSON so it no longer matches the original key; delayed download long after the inbound message was processed; caller cancels the request before invoking the fetch; runtime replaced and fetcher map cleared.","solutions":["Download the inline image during/soon after inbound admission while the registered fetcher is still alive","Pass the exact same Attachment object reference produced by admission (not a cloned/deserialized copy)","Check signal.aborted before calling and create a fresh AbortController for a new attempt","If the runtime was replaced, re-admit the message or obtain a new download handle"],"exampleFix":"// before\nconst buf = await runtime.fetchTeamsInlineImage(clonedAttachment, signal);\n// after\nif (!signal.aborted) {\n  const buf = await runtime.fetchTeamsInlineImage(originalAttachment, signal);\n}","handlingStrategy":"try-catch","validationCode":"if (signal.aborted || !runtime.hasTeamsInlineImageFetcher(attachment)) return null;","typeGuard":null,"tryCatchPattern":"try { return await runtime.fetchTeamsInlineImage(attachment, signal); } catch { return placeholderBuffer; }","preventionTips":["Download inline images during or immediately after inbound admission","Pass the original attachment reference, never a cloned/deserialized copy","Check signal.aborted before each download and use a fresh AbortController per attempt"],"tags":["teams","attachments","ephemeral-state","abort"],"backgroundTag":"resource-not-found","analyzedSha":"01ad8584922b5d85292b1723cae71fa0d9b07a19","analyzedAt":"2026-09-10T03:14:50.855Z","contentChangedAt":"2026-09-10T03:14:50.855Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}