paperclipai/paperclip · error · DiscordAdapterCompatibilityError

ensureDiscordRootThread was called for a non-Discord endpoin

Error message

ensureDiscordRootThread was called for a non-Discord endpoint

What it means

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.

Source

Thrown at server/src/services/chat-sdk-runtime.ts:2619

  async getUser(user: string | Author): Promise<UserInfo | null> {
    return await this.chat.getUser(user);
  }

  async abortTurn(threadId: string): Promise<void> {
    await this.chat.abortTurn(threadId);
  }

  getProviderAdapter(): Adapter {
    return this.adapter;
  }

  async ensureDiscordRootThread(input: {
    channelId: string;
    content: string;
    messageId: string;
  }): Promise<void> {
    if (this.provider !== "discord") {
      throw new DiscordAdapterCompatibilityError(
        "ensureDiscordRootThread was called for a non-Discord endpoint",
      );
    }
    const discord = this.adapter as unknown as DiscordAdapterInternals & {
      ensureRootThread?: (
        channelId: string,
        messageId: string,
        content: string,
      ) => Promise<unknown>;
    };
    if (typeof discord.ensureRootThread !== "function") {
      throw new DiscordAdapterCompatibilityError(
        "ensureRootThread is unavailable",
      );
    }
    await discord.ensureRootThread.call(
      this.adapter,
      input.channelId,

View on GitHub (pinned to 01ad858492)

Solutions

  1. Check runtime provider === 'discord' before calling ensureDiscordRootThread and skip/branch for other providers.
  2. Fix endpoint configuration or event routing so Discord root-thread bootstrap only runs on Discord endpoints.
  3. Move the ensure-root-thread step behind a provider dispatch table instead of calling Discord-specific APIs directly.

Example fix

// before
await runtime.ensureDiscordRootThread({ channelId, content, messageId });

// after
if (runtime.provider === "discord") {
  await runtime.ensureDiscordRootThread({ channelId, content, messageId });
}
Defensive patterns

Strategy: validation

Validate before calling

if (runtime.provider !== "discord") return; // root-thread bootstrap is Discord-only

Type guard

function isDiscordRuntime(r: { provider: string }): boolean { return r.provider === "discord"; }

Try / catch

try {
  await runtime.ensureDiscordRootThread({ channelId, content, messageId });
} catch (err) {
  if (err instanceof DiscordAdapterCompatibilityError && err.message.includes("non-Discord endpoint")) {
    // skip bootstrap on non-Discord providers
  } else throw err;
}

Prevention

When it happens

Trigger: Calling ensureDiscordRootThread({channelId, content, messageId}) on a runtime bound to a non-Discord provider (telegram, slack, microsoft-teams, etc.).

Common situations: 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.

Understand the failure class

Background: UnsupportedOperationException and "is not supported" errors: when a library deliberately refuses a call — this error's family across 30 libraries.

Related errors


AI-assisted analysis of paperclipai/paperclip@01ad858492 (2026-09-10). Data as JSON: /api/errors/e2ff0b76aa9c2af0. Report an issue: GitHub.