Back to Copilotkit

SKILL

skills/debug-and-troubleshoot/SKILL.md

1.57.47.4 KB
Original Source

Setup

Debug in layers: server debug first, then client debug, then the web inspector. Handle errors in onError using CopilotKitCoreErrorCode string literals (snake_case).

Server debug

ts
// app/routes/api.copilotkit.$.tsx
import { CopilotRuntime } from "@copilotkit/runtime/v2";

const runtime = new CopilotRuntime({
  agents,
  debug:
    process.env.NODE_ENV !== "production"
      ? { events: true, lifecycle: true, verbose: true }
      : false,
});

Client debug + onError

tsx
import { CopilotKitProvider, CopilotChat } from "@copilotkit/react-core/v2";

<CopilotKitProvider
  runtimeUrl="/api/copilotkit"
  showDevConsole="auto"
  debug={process.env.NODE_ENV !== "production"}
  onError={({ error, code, context }) => {
    // central telemetry; keep UI toasts on the chat:
    telemetry.captureException(error, { tags: { code }, extra: context });
  }}
>
  <CopilotChat
    agentId="default"
    onError={({ code }) => {
      if (code === "agent_thread_locked") {
        toast({ title: "Agent busy — try again in a moment" });
      }
    }}
  />
</CopilotKitProvider>;

Web inspector (dev)

tsx
<CopilotKitProvider
  runtimeUrl="/api/copilotkit"
  showDevConsole="auto"
  debug={{ events: true, lifecycle: true }}
/>

showDevConsole="auto" mounts the inspector in development. It lazy-loads @copilotkit/web-inspector via @lit-labs/react — zero cost in prod.

Core Patterns

Diagnose runtime_info_fetch_failed

Checks in order:

  1. runtimeUrl starts with a leading / or is a full origin.
  2. /info is reachable:
bash
curl -i http://localhost:3000/api/copilotkit/info
  1. CORS allows the browser origin (cross-origin deployments need createCopilotRuntimeHandler({ cors: true }) or proxy-level CORS).
  2. Cookie auth: credentials="include" on the provider AND CORS configured to allow credentials.

Diagnose agent_not_found

  • Server agents: { default: ... } key matches the client <CopilotChat agentId="default"> / useAgent({ agentId }) string.
  • /info JSON lists the expected agent names.

Diagnose agent_thread_locked

Double-submit or concurrent run. Handle it:

tsx
<CopilotKitProvider
  runtimeUrl="/api/copilotkit"
  onError={({ code }) => {
    if (code === "agent_thread_locked") {
      toast.warning("Agent is busy — please wait");
    }
  }}
/>

Trace missing AG-UI events — server first, client second

Dev tools Network tab on the /run SSE stream shows each event frame. Server debug produces Pino logs with every event emitted. If the event is missing in the Pino logs, the agent factory isn't yielding it — fix the agent. If it's in the Pino logs but not the browser, check the SSE connection isn't being buffered (proxies, compression).

Deprecated-alias cheat sheet

Safe aliases — mechanical find/replace, behavior unchanged:

Deprecated / aliasCanonical
publicLicenseKey (alias)publicApiKey (canonical; resolution is publicApiKey ?? publicLicenseKey)
agents__unsafe_dev_only(no prod alias — use runtimeUrl or publicApiKey)
selfManagedAgents(no prod alias — same as above)
createCopilotEndpoint* aliases (still accepted)createCopilotRuntimeHandler
createCopilotExpressHandler / createCopilotHonoHandlermount createCopilotRuntimeHandler in the framework's native route
beforeRequestMiddlewarehooks.onRequest (both run pre-dispatch, before route resolution)
afterRequestMiddlewarehooks.onResponse (both run post-handler, on the outbound Response)

Renamed props (breaking — semantics changed, not just names):

Old propNew propWhy it's breaking
imageUploadsEnabledattachments={{ enabled: true }}attachments covers the broader file/paste/drag surface, not just image uploads; the shape is an object, not a boolean.

Common Mistakes

CRITICAL checking for v1 SCREAMING_SNAKE codes in v2

Wrong:

ts
onError: ({ code }) => {
  if (code === "API_NOT_FOUND") {
    /* never matches */
  }
};

Correct:

ts
onError: ({ code }) => {
  if (code === "runtime_info_fetch_failed") {
    /* matches */
  }
};

v2 codes are snake_case on CopilotKitCoreErrorCode (runtime_info_fetch_failed, agent_run_failed, agent_thread_locked, tool_handler_failed, …). v1 SCREAMING_SNAKE values never match v2.

Source: packages/core/src/core/core.ts:71-105

HIGH chasing missing events client-side only

Wrong:

tsx
// turning on client debug and puzzling over missing events
<CopilotKitProvider debug={{ events: true, verbose: true }} />

Correct:

ts
// turn on server debug FIRST
new CopilotRuntime({
  agents,
  debug: { events: true, lifecycle: true, verbose: true },
});

Server drops events too; Pino server logs are more reliable as the first trace point. If the event is in Pino but not the browser, then look at the SSE stream in the Network tab.

Source: docs/snippets/shared/troubleshooting/debug-mode.mdx:62-69,129-141

HIGH not handling agent_thread_locked

Wrong:

tsx
<CopilotKitProvider runtimeUrl="/api/copilotkit" />
// no onError — double-submit shows a scary error banner

Correct:

tsx
<CopilotKitProvider
  runtimeUrl="/api/copilotkit"
  onError={({ code }) => {
    if (code === "agent_thread_locked") {
      return toast({ title: "Agent busy — try again in a moment" });
    }
  }}
/>

agent_thread_locked is the common concurrent-run error — treat it as a user-facing busy signal, not a crash.

Source: packages/core/src/core/core.ts:81-97

MEDIUM debug:true expecting full payload logs

Wrong:

tsx
<CopilotKitProvider debug={true} />
// expecting every event payload in the console — only gets summaries

Correct:

tsx
<CopilotKitProvider debug={{ events: true, lifecycle: true, verbose: true }} />

Boolean true enables events + lifecycle summaries but keeps verbose: false. Verbose is opt-in because it may log PII.

Source: docs/snippets/shared/troubleshooting/debug-mode.mdx:85-93

MEDIUM duplicate onError side effects (provider + chat)

Wrong:

tsx
<CopilotKitProvider onError={toast}>
  <CopilotChat onError={toast} />
</CopilotKitProvider>

Correct:

tsx
<CopilotKitProvider onError={telemetry}>
  <CopilotChat onError={toast} />
</CopilotKitProvider>

Chat onError fires IN ADDITION TO provider onError — double toasts if both trigger UI. Canonical split: telemetry on provider, UI on chat.

Source: docs/snippets/shared/troubleshooting/error-debugging.mdx:56-70

References