{"record":{"id":"e2ff0b76aa9c2af0","repo":"paperclipai/paperclip","slug":"ensurediscordrootthread-was-called-for-a-non-disco","errorCode":null,"errorMessage":"ensureDiscordRootThread was called for a non-Discord endpoint","messagePattern":"ensureDiscordRootThread was called for a non-Discord endpoint","errorType":"exception","errorClass":"DiscordAdapterCompatibilityError","httpStatus":null,"severity":"error","filePath":"server/src/services/chat-sdk-runtime.ts","lineNumber":2619,"sourceCode":"  async getUser(user: string | Author): Promise<UserInfo | null> {\n    return await this.chat.getUser(user);\n  }\n\n  async abortTurn(threadId: string): Promise<void> {\n    await this.chat.abortTurn(threadId);\n  }\n\n  getProviderAdapter(): Adapter {\n    return this.adapter;\n  }\n\n  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,","sourceCodeStart":2601,"sourceCodeEnd":2637,"githubUrl":"https://github.com/paperclipai/paperclip/blob/01ad8584922b5d85292b1723cae71fa0d9b07a19/server/src/services/chat-sdk-runtime.ts#L2601-L2637","documentation":"ensureDiscordRootThread creates a Discord forum/root thread via the Discord adapter's ensureRootThread extension. Because that capability is Discord-specific, the runtime asserts this.provider === 'discord' first and throws a DiscordAdapterCompatibilityError for any other provider.","triggerScenarios":"Calling ensureDiscordRootThread({channelId, content, messageId}) on a runtime bound to a non-Discord provider (telegram, slack, microsoft-teams, etc.).","commonSituations":"Provider-agnostic bootstrap code that unconditionally ensures a root thread; endpoints reconfigured from Discord to another provider while Discord-specific flows still run; event routing bugs delivering a Discord-shaped event to a non-Discord endpoint.","solutions":["Check runtime provider === 'discord' before calling ensureDiscordRootThread and skip/branch for other providers.","Fix endpoint configuration or event routing so Discord root-thread bootstrap only runs on Discord endpoints.","Move the ensure-root-thread step behind a provider dispatch table instead of calling Discord-specific APIs directly."],"exampleFix":"// before\nawait runtime.ensureDiscordRootThread({ channelId, content, messageId });\n\n// after\nif (runtime.provider === \"discord\") {\n  await runtime.ensureDiscordRootThread({ channelId, content, messageId });\n}","handlingStrategy":"validation","validationCode":"if (runtime.provider !== \"discord\") return; // root-thread bootstrap is Discord-only","typeGuard":"function isDiscordRuntime(r: { provider: string }): boolean { return r.provider === \"discord\"; }","tryCatchPattern":"try {\n  await runtime.ensureDiscordRootThread({ channelId, content, messageId });\n} catch (err) {\n  if (err instanceof DiscordAdapterCompatibilityError && err.message.includes(\"non-Discord endpoint\")) {\n    // skip bootstrap on non-Discord providers\n  } else throw err;\n}","preventionTips":["Only call Discord-specific bootstrap from Discord-dispatched code paths.","Fix event routing so Discord-shaped events never reach non-Discord runtimes.","Use a provider dispatch table instead of direct Discord API calls in shared flows."],"tags":["discord","adapter-compatibility","thread-bootstrap","provider-guard"],"backgroundTag":"unsupported-operation","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"}