{"record":{"id":"f91af8ca25a0d160","repo":"paperclipai/paperclip","slug":"ensurerootthread-is-unavailable","errorCode":null,"errorMessage":"ensureRootThread is unavailable","messagePattern":"ensureRootThread is unavailable","errorType":"exception","errorClass":"DiscordAdapterCompatibilityError","httpStatus":null,"severity":"error","filePath":"server/src/services/chat-sdk-runtime.ts","lineNumber":2631,"sourceCode":"  async ensureDiscordRootThread(input: {\n    channelId: string;\n    content: string;\n    messageId: string;\n  }): Promise<void> {\n    if (this.provider !== \"discord\") {\n      throw new DiscordAdapterCompatibilityError(\n        \"ensureDiscordRootThread was called for a non-Discord endpoint\",\n      );\n    }\n    const discord = this.adapter as unknown as DiscordAdapterInternals & {\n      ensureRootThread?: (\n        channelId: string,\n        messageId: string,\n        content: string,\n      ) => Promise<unknown>;\n    };\n    if (typeof discord.ensureRootThread !== \"function\") {\n      throw new DiscordAdapterCompatibilityError(\n        \"ensureRootThread is unavailable\",\n      );\n    }\n    await discord.ensureRootThread.call(\n      this.adapter,\n      input.channelId,\n      input.messageId,\n      input.content,\n    );\n  }\n\n  /**\n   * Reuse the pinned provider parser when a Telegram slash command also\n   * carries media in its caption. The Chat SDK exposes slash-command fields\n   * separately from the parsed Message, so without this bridge Paperclip\n   * would silently discard a captioned document/photo/audio/video.\n   */\n  parseTelegramCommandMessage(raw: unknown): Message | null {","sourceCodeStart":2613,"sourceCodeEnd":2649,"githubUrl":"https://github.com/paperclipai/paperclip/blob/01ad8584922b5d85292b1723cae71fa0d9b07a19/server/src/services/chat-sdk-runtime.ts#L2613-L2649","documentation":"On a Discord endpoint, ensureDiscordRootThread still requires the adapter to expose an ensureRootThread(channelId, messageId, content) extension. If the wired Discord adapter does not define it, the runtime cannot create the root thread and throws a DiscordAdapterCompatibilityError rather than failing silently.","triggerScenarios":"Calling ensureDiscordRootThread on a Discord runtime whose adapter lacks ensureRootThread — e.g. an older Discord adapter build, a custom adapter, or a test double that only mocks message posting.","commonSituations":"Discord adapter version pinned before the root-thread support landed; hand-rolled Discord adapters; unit-test stubs missing the extension method.","solutions":["Upgrade the Discord adapter to a version implementing ensureRootThread.","Feature-detect discord.ensureRootThread before calling and handle the missing capability (skip bootstrap or post to the channel directly).","Update test doubles to include ensureRootThread."],"exampleFix":"// before\nawait runtime.ensureDiscordRootThread({ channelId, content, messageId });\n\n// after\nconst discord = runtime.getProviderAdapter() as { ensureRootThread?: unknown };\nif (typeof discord.ensureRootThread === \"function\") {\n  await runtime.ensureDiscordRootThread({ channelId, content, messageId });\n} else {\n  await runtime.channel(channelId).post(content);\n}","handlingStrategy":"type-guard","validationCode":"const discord = runtime.getProviderAdapter() as Record<string, unknown>;\nif (typeof discord.ensureRootThread !== \"function\") throw new Error(\"discord adapter lacks ensureRootThread\");","typeGuard":"function supportsRootThreads(a: unknown): a is { ensureRootThread: (channelId: string, messageId: string, content: string) => Promise<unknown> } {\n  return typeof a === \"object\" && a !== null && typeof (a as any).ensureRootThread === \"function\";\n}","tryCatchPattern":"try {\n  await runtime.ensureDiscordRootThread({ channelId, content, messageId });\n} catch (err) {\n  if (err instanceof DiscordAdapterCompatibilityError && err.message.includes(\"ensureRootThread is unavailable\")) {\n    await runtime.channel(channelId).post(content);\n  } else throw err;\n}","preventionTips":["Pin the Discord adapter to a version implementing ensureRootThread.","Feature-detect ensureRootThread before root-thread bootstrap and fall back to channel posting.","Include ensureRootThread in Discord adapter test doubles."],"tags":["discord","adapter-compatibility","thread-bootstrap","missing-capability"],"backgroundTag":"method-not-implemented","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"}