# Troubleshooting

> Documented failure modes with fixes: the duplicate @ag-ui/client "_debug private property" compile error, TS1309 from missing type: module, TS2345 ModalView narrowing, JSX compiled against React, setup_required and Waiting for runtime states, ready() resolving on a degraded Channel, Unknown option '--skill' from a stale CLI, and the invented Bot-prefixed APIs that never existed.

- Repository: CopilotKit/channels-sdk
- GitHub: https://github.com/CopilotKit/channels-sdk
- Human docs: https://grok-wiki.com/public/docs/copilotkit-channels-sdk-4c947e3e6161
- Complete Markdown: https://grok-wiki.com/public/docs/copilotkit-channels-sdk-4c947e3e6161/llms-full.txt

## Source Files

- `.agents/skills/build-channels-agent/SKILL.md`
- `AGENTS.md`
- `.agents/skills/build-channels-agent/references/ui-components.md`
- `.agents/skills/build-channels-agent/references/hitl-patterns.md`
- `README.md`

---

---
title: "Troubleshooting"
description: "Documented failure modes with fixes: the duplicate @ag-ui/client \"_debug private property\" compile error, TS1309 from missing type: module, TS2345 ModalView narrowing, JSX compiled against React, setup_required and Waiting for runtime states, ready() resolving on a degraded Channel, Unknown option '--skill' from a stale CLI, and the invented Bot-prefixed APIs that never existed."
---

Every failure mode on this page is documented in the repository's `build-channels-agent` skill, `AGENTS.md`, or the README, with the exact error text and the verified fix. They fall into three groups: compile-time errors that block a correct-looking project from typechecking, runtime and lifecycle states where a process starts cleanly but never answers, and tooling errors from stale CLI or hallucinated API names.

## Quick index

| Symptom | Cause | Fix |
| --- | --- | --- |
| `separate declarations of a private property '_debug'` | Two nested copies of `@ag-ui/client` | Pin one version with `overrides` |
| `TS1309: The current file is a CommonJS module` | Missing `"type": "module"` with top-level `await` | `npm pkg set type=module` |
| `TS2345: ... 'ChannelNode' is not assignable to ... 'ModalView'` | `<Modal>` written as JSX | Call `Modal({...})` as a function |
| JSX errors referencing React types | Missing `jsxImportSource` in tsconfig | Point it at `@copilotkit/channels` |
| Status stuck at `setup_required` | Channel declared but provider unprovisioned | Finish provider setup in Intelligence — do not switch to a direct adapter |
| Dashboard shows **Waiting for runtime** | `createChannel({ name })` does not match the Channel Code | Fix the `name` / `CHANNEL_CODE` mismatch |
| Process starts, serves HTTP 200, answers nothing | `ready()` resolved on a degraded Channel | Gate startup on `status().overall === "online"` |
| `Unknown option '--skill'` | Stale `copilotkit` CLI on PATH or in the npx cache | Always run `npx copilotkit@latest ...` |
| `createBot` / `defineBotTool` fail to compile | Pre-release names that shipped nowhere | Use `createChannel` / `defineChannelTool` |

## Compile-time failures

### Duplicate `@ag-ui/client`: the `_debug` private property error

Passing any agent to `createChannel({ agent })` fails with a confusing error about *"separate declarations of a private property `_debug`"*. This is the single most likely reason a correct-looking Channel refuses to typecheck.

`@copilotkit/channels` and `@copilotkit/runtime` both depend on one exact version of `@ag-ui/client`, but a transitive dependency (`@ag-ui/mcp-middleware`) pulls an older one and npm nests it. Two copies mean two separate `AbstractAgent` class declarations, and TypeScript treats their private fields as incompatible.

Fix by pinning one copy in `package.json`:

```json
{ "overrides": { "@ag-ui/client": "0.0.57" } }
```

Use the version Runtime declares — `npm ls @ag-ui/client` shows both copies — then reinstall. pnpm uses `pnpm.overrides`; yarn uses `resolutions`.

### TS1309: missing `"type": "module"`

The documented listener uses top-level `await` (`await channels.ready(...)`), which requires ESM. Without `"type": "module"` in `package.json` the compile fails:

```
TS1309: The current file is a CommonJS module
```

Fix:

```sh
npm pkg set type=module
```

This step is part of the documented install sequence, alongside installing `@copilotkit/channels` and `@copilotkit/runtime` as a version-locked pair.

### TS2345: `<Modal>` JSX loses the `ModalView` type

The Channels JSX runtime declares `JSX.Element = ChannelNode`, so every JSX expression is typed `ChannelNode`. That erases the `ModalView` narrowing that `ctx.openModal` requires, and `<Modal .../>` fails under `strict`:

```
error TS2345: Argument of type 'ChannelNode' is not assignable to parameter of type 'ModalView'.
```

Call `Modal` as a plain function and pass `children` as a prop. The children can still be JSX — only the root must stay a `ModalView`:

```tsx
channel.onCommand("feedback", async ({ openModal }) => {
  await openModal?.(
    Modal({
      callbackId: "feedback",
      title: "Send feedback",
      submitLabel: "Send",
      children: [
        <TextInput id="body" label="What happened?" multiline />,
        <RadioButtons id="severity" label="Severity">
          <ModalSelectOption label="Blocking" value="high" />
          <ModalSelectOption label="Annoying" value="low" />
        </RadioButtons>,
      ],
    }),
  );
});
```

Keep the `?.` on `openModal` — it is `undefined` on surfaces with no modal trigger.

### JSX compiled against React

A file containing Channels JSX must be `.tsx`, and the tsconfig must point the JSX factory at Channels. Without `jsxImportSource` the tree compiles against React's JSX types and fails:

```json
{
  "compilerOptions": {
    "target": "ES2022",
    "jsx": "react-jsx",
    "jsxImportSource": "@copilotkit/channels",
    "module": "nodenext",
    "moduleResolution": "nodenext",
    "strict": true,
    "types": ["node"]
  }
}
```

<Warning>
Point `jsxImportSource` at `@copilotkit/channels` — the package you installed. Do not point it at `@copilotkit/channels-ui` unless that package is a direct dependency: it is only a transitive dependency of the umbrella package, so the import resolves under npm's hoisted layout but fails under pnpm's isolated one.
</Warning>

### The Bot-prefixed APIs that never existed

`createBot`, `defineBotTool`, `defineBotCommand`, and `BotToolContext` come from a pre-release naming scheme and exist nowhere in the shipped packages — importing them fails to compile. The word "bot" is not part of this API. The same applies to `new Bot()` and `channel.on("message", ...)`.

| Invented name | Real API |
| --- | --- |
| `createBot` | `createChannel` |
| `defineBotTool` | `defineChannelTool` |
| `defineBotCommand` | `defineChannelCommand` |
| `BotToolContext` | `ChannelToolContext` |
| `channel.on("message", fn)` | `channel.onMessage(fn)` / `channel.onMention(fn)` |
| `channel.start()` / `channel.stop()` | No public lifecycle method — attach the Channel to `CopilotRuntime` and create a listener |

Coding agents drafting from memory are the usual source of these names. Ground generated code in the repository's `.agents/skills/build-channels-agent/SKILL.md` rather than in recalled Slack/Discord SDK patterns.

## Runtime and lifecycle states

### Status stuck at `setup_required`

`setup_required` means the Channel is declared but unprovisioned — the provider side (Slack app, Teams app, credentials) has not been finished in CopilotKit Intelligence. The fix is to complete that setup in the Intelligence dashboard; the `setup-slack-channel` skill (installed via `npx copilotkit@latest skills install --skill setup-slack-channel`) covers diagnosing it for Slack.

<Warning>
Swapping to a direct adapter (`adapters: [slack({ botToken, appToken })]`) to escape `setup_required` is itself a documented failure mode, not a fallback. The direct path puts platform secrets in your process and still requires Intelligence — it does not bypass the managed setup. Fix the managed configuration instead.
</Warning>

### Dashboard shows **Waiting for runtime**

`createChannel({ name })` must equal the exact Channel Code from Intelligence: 3–64 characters, starting with a lowercase letter, lowercase letters and digits separated by single hyphens, project-unique, and never the literal `channels`. A mismatch leaves the Channel at **Waiting for runtime** in the dashboard.

The name is validated by the runtime, not by `createChannel`, so a typo fails at startup rather than at the call site. Note the two vocabularies are distinct: `channels.status()` returns SDK values (`connecting`, `online`, `setup_required`, `reconnecting`, `error`, `stopped`), while the Intelligence dashboard uses its own states (Disabled, Setup incomplete, Setup failed, Waiting for runtime, Conflict, Offline, Delivery failing, Online).

### `ready()` resolves on a degraded Channel

`channels.ready()` is not proof of life. It settles activation, but it also resolves on `setup_required`, because a declared-but-unprovisioned Channel is a valid degraded state. Without a status check you get a process that starts cleanly, serves HTTP 200, and answers nothing.

Gate startup on the actual status:

```ts
await channels.ready({ timeoutMs: 30_000 });

const status = channels.status();
if (status.overall !== "online") {
  throw new Error(`Channel is not online: ${JSON.stringify(status)}`);
}

server.listen(Number(process.env.PORT ?? 3000));
```

Activation errors are recorded as status and surfaced through `ready()` / `status()` — they are not thrown. Only an up-front misconfiguration (a duplicate or missing Channel name) throws synchronously. On `error`, `ready()` rejects with the cause.

```mermaid
stateDiagram-v2
    [*] --> connecting : listener created
    connecting --> online : activation succeeds
    connecting --> setup_required : declared but unprovisioned
    connecting --> error : activation fails
    online --> reconnecting : gateway socket drops
    reconnecting --> online : retry succeeds
    online --> stopped : channels.stop()
    setup_required --> stopped : channels.stop()

    note right of setup_required
        ready() RESOLVES here too —
        gate on status().overall === "online"
    end note
    note right of error
        ready() rejects with the cause
    end note
```

<Tip>
To verify the full round trip without spending a model call, use the minimal-channel trick: have `onMention` post a single `🪁` instead of running the agent. Once the kite lands in the thread, swap the handler body for `thread.runAgent(...)`.
</Tip>

## Tooling failures

### `Unknown option '--skill'` from a stale CLI

```sh
npx copilotkit@latest skills install --skill setup-slack-channel -y
```

If this fails with `Unknown option '--skill'`, an older `copilotkit` — globally installed or left in the npx cache — is shadowing the current CLI. Keep the `@latest`; that is what forces npx to fetch the current version instead of reusing what is already on PATH or cached. A bare `npx copilotkit` resolves to whatever is already available.

The same rule applies to `npx copilotkit@latest channels setup` and every other CLI invocation this repository documents.

### Vendored skill drift

This repository once vendored a copy of `setup-slack-channel` under `.agents/skills/`. It fell ~16 KB behind the canonical copy in `CopilotKit/CopilotKit` and ended up asserting the opposite of the truth about Slack interactivity, and `skills install` overwrote the eight tracked files. The skill now has one home and is fetched on demand — install it via the CLI rather than copying it into a project.

## Related pages

<CardGroup cols={2}>
  <Card title="Installation" href="/installation">
    The version-locked package pair, the `overrides` pin for `@ag-ui/client`, ESM enforcement, and the required tsconfig.
  </Card>
  <Card title="Channel lifecycle and status" href="/channel-lifecycle">
    The six SDK status values versus the Intelligence dashboard states, `ready()` semantics, and teardown ordering.
  </Card>
  <Card title="Managed Channels vs direct adapters" href="/managed-vs-direct">
    Why switching to a direct adapter to escape `setup_required` is a known failure mode.
  </Card>
  <Card title="Slash commands and modals" href="/slash-commands-and-modals">
    The `Modal({...})` function-call pattern, `callbackId` routing, and `onModalSubmit` / `onModalClose`.
  </Card>
  <Card title="Set up with a coding agent" href="/coding-agent-setup">
    The `npx copilotkit@latest channels setup` path and the hosted channels-guide.md workflow.
  </Card>
  <Card title="Minimal Channel example" href="/minimal-channel-example">
    The smallest complete listener, including the 🪁 no-model-call verification trick.
  </Card>
</CardGroup>
