| name | debug-and-troubleshoot |
| description | End-to-end debugging for CopilotKit v2 — CopilotKitCoreErrorCode catalog (17 snake_case codes including runtime_info_fetch_failed, agent_thread_locked, agent_run_failed, tool_handler_failed, etc.), TranscriptionErrorCode catalog (9 codes), AG-UI SSE event tracing, web inspector lazy-loading, onError wiring on both CopilotKitProvider and CopilotChat, server-first debug discipline, and deprecated-alias → canonical cheat sheet. v1 CopilotKitErrorCode (SCREAMING_SNAKE) is kept for migration context only. Load when diagnosing a CopilotKit runtime or client failure, when interpreting an error code, when tracing missing events, or when wiring onError handlers.
|
| type | lifecycle |
| library | copilotkit |
| library_version | 1.56.2 |
| requires | ["copilotkit/react-core"] |
| sources | ["CopilotKit/CopilotKit:packages/core/src/core/core.ts","CopilotKit/CopilotKit:packages/shared/src/utils/errors.ts","CopilotKit/CopilotKit:packages/shared/src/transcription-errors.ts","CopilotKit/CopilotKit:packages/web-inspector/src/index.ts","CopilotKit/CopilotKit:docs/snippets/shared/troubleshooting/common-issues.mdx","CopilotKit/CopilotKit:docs/snippets/shared/troubleshooting/error-debugging.mdx","CopilotKit/CopilotKit:docs/snippets/shared/troubleshooting/debug-mode.mdx"] |
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
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
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)
<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:
runtimeUrl starts with a leading / or is a full origin.
/info is reachable:
curl -i http://localhost:3000/api/copilotkit/info
- CORS allows the browser origin (cross-origin deployments need
createCopilotRuntimeHandler({ cors: true }) or proxy-level CORS).
- 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:
<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 / alias | Canonical |
|---|
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 / createCopilotHonoHandler | mount createCopilotRuntimeHandler in the framework's native route |
beforeRequestMiddleware | hooks.onRequest (both run pre-dispatch, before route resolution) |
afterRequestMiddleware | hooks.onResponse (both run post-handler, on the outbound Response) |
Renamed props (breaking — semantics changed, not just names):
| Old prop | New prop | Why it's breaking |
|---|
imageUploadsEnabled | attachments={{ 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:
onError: ({ code }) => {
if (code === "API_NOT_FOUND") {
}
};
Correct:
onError: ({ code }) => {
if (code === "runtime_info_fetch_failed") {
}
};
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:
<CopilotKitProvider debug={{ events: true, verbose: true }} />
Correct:
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:
<CopilotKitProvider runtimeUrl="/api/copilotkit" />
Correct:
<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:
<CopilotKitProvider debug={true} />
Correct:
<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:
<CopilotKitProvider onError={toast}>
<CopilotChat onError={toast} />
</CopilotKitProvider>
Correct:
<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