Skip to content

Fix common agent problems

Start with four checks, in order:

  1. Project discovery: agent-sdk validate --dir .
  2. Whether the serve process is running
  3. What the playground or HTTP API shows
  4. The session event stream (trace)

Match your symptom below. Keep the commands as agent-sdk; see Run the CLI if you still need an alias.

What if serve or the playground looks wrong?

What you seeWhat to do
serve won't startRun agent-sdk validate --dir . and fix the reported errors.
Playground is blank or says there are no agentsThe UI needs a running serve process. Building the playground assets alone is not enough.
Edits to the playground don't show upUse serve --dev and open the printed playground HMR URL (often port 5273), not only the static :3000 URL.
Sessions exist on disk but the playground list is emptyThe list shows sessions for the authenticated caller. In --dev on loopback the list is wider. Otherwise open /<slug>/playground?sessionId=ses_… or inspect sessions/ under your state root.
Port 3000 or 5273 is already in useFor the default serve port, the CLI tries the next free port and prints a notice. Pass --port to pick one, or --port 0 for any free port. Stop leftover Vite or webhook-forwarder processes if you need the original port.

What if a model turn goes wrong?

What you seeWhat to do
Built-in file reads and greps fail; the turn retries for a long timeRun under Node 22.13+ (or tsx), never Bun. Look for NGHTTP2_FRAME_SIZE_ERROR in logs.
The turn fails immediately with an API-key errorSign in with agent-sdk login, or set CURSOR_API_KEY. Discovery, info, call, and serve bring-up work without a key; model turns need one.
Replies quote rules or AGENTS.md from outside your agent projectThe session workspace inherited parent-folder config. Point defineAgent({ local: { cwd } }) outside that tree, or set --state-root to a clean directory (for example under /tmp).
Server tools, skills, or workspace seed files never appearThe agent runtime is cloud. Those features apply on the local runtime. validate warns when this combination is present.
validate and run succeed, but typecheck fails in CIThe CLI runs TypeScript with type-stripping only. Keep tool execute return types as object literals or type aliases, not interface types.
Login works, but turns are rejected when using custom API hostsPoint login and model traffic at the same host (CURSOR_API_BASE_URL and CURSOR_BACKEND_URL). A key from one host is rejected by the other.

What if the HTTP API returns an error?

What you seeWhat to do
409 on a follow-up messageRefresh the continuationToken (it rotates on each accepted follow-up), wait if the session is busy, or confirm the session is a chat session (task and schedule sessions are not followable).
409 session_busy on call --sessionWait for the model turn to finish, or omit --session for a one-off call.
403 on stream or follow-upUse the same auth identity that created the session. Off localhost, pass --bearer-token and send it on every request.
Works on localhost; blocked through a tunnel or LANDefault auth allows only direct loopback callers. Share the host with --bearer-token <secret> (or authored bearerAuth). Use --allow-anonymous only on a trusted private network.
A channel route fails to compile with a schema type errorGET routes need a Zod querySchema. POST / PUT / PATCH need a Zod bodySchema. Use z.object({}) or z.unknown() for open shapes.

What if GitHub webhooks misbehave?

What you seeWhat to do
github forward returns 401 on every delivery, but the hook was createdClear GITHUB_TOKEN and GH_TOKEN for that command. The forwarder uses your gh CLI login: GITHUB_TOKEN= GH_TOKEN= agent-sdk github forward …
Hook already exists when starting a forwarderGitHub allows one forwarder per repo. Run a single github forward --dir <parent> and stop stale forwarders.
Deliveries rejected outside --devSet GITHUB_WEBHOOK_SECRET on the server and on the signer. Without a secret, the channel stays loopback-only.
You lack repo admin and can't forwardUse agent-sdk github replay <pr-url>. It needs pull access only and posts signed test payloads.

What if Slack stays quiet?

What you seeWhat to do
Logs show channel idle … missing credentialsExpected when tokens are missing. Run agent-sdk slack create --dir <agent> to provision the app and write the tokens, or agent-sdk slack init --manual --dir <agent> and paste the manifests at api.slack.com. Then set <PREFIX>_SLACK_BOT_TOKEN and <PREFIX>_SLACK_APP_TOKEN per agent and run agent-sdk slack doctor --prefix <PREFIX>.
slack create reports the app needs admin approvalOpen Slack's Request approval page (the CLI prints the link; the same URL is Send a reminder after you submit). Managed install does not file the request. Keep the CLI running, then click Retry in the dashboard after an admin approves.
The bot ignores ordinary channel postsDefault engagement is mentions and DMs only. Enable engagement.channelPosts with an allowlist, and subscribe the app to message.channels / message.groups.
Approve / Deny buttons do nothingChannels that post approval cards need toolApprovals: true. Recreate the app with slack create if interactivity is off.

What if host MCP OAuth fails?

What you seeWhat to do
must be defineConnection({ url, oauth: true })The connection file needs oauth: true, or you passed the wrong connection name to agent-sdk mcp oauth.
Local auth works; hosted calls unauthorizedRun agent-sdk mcp oauth <name> --store, confirm names with agent-sdk secrets list <slug>, then redeploy.
Model asks for mcp_auth or IDE MCP for a privileged serverThat connection is likely hostOnly. Call it from a host tool via ctx.host.mcp, and update instructions.

See Host MCP OAuth and skills/mcp-auth/SKILL.md.

What if a secret showed up in a terminal transcript?

What you seeWhat to do
secrets set … NAME=VALUE in an agent-captured terminal or shell historyRotate the secret at the provider. Set it again with names only: agent-sdk secrets set <slug> NAME (hidden prompt) or pipe/redirect the value. NAME=VALUE requires --from-argv and still leaks into argv.
Alias token printed during first deploy or rotate-tokenTreat it as exposed if the transcript left your machine. Run agent-sdk rotate-token <slug>, store the new token outside agent transcripts, and update callers.
Someone verified a secret with echo / printenvRotate it. Confirm presence with agent-sdk secrets list <slug> (names only), then redeploy and test the feature.

What if schedules, reminders, or approvals stall?

What you seeWhat to do
A schedule or reminder never fires under --devDev mode does not auto-fire. Trigger with POST /<slug>/v1/dev/schedules/<id> or POST /<slug>/v1/dev/reminders/<id> (list reminders at GET /v1/dev/reminders).
A pending tool approval disappeared after restartParked approvals do not survive host restart. They resolve as interrupted. Run the turn again.
A reminder is disarmed after restart (handler_lost_on_restart)Handler-form reminders live in memory. Re-arm them from the code that created them, or use prompt-form reminders.

How do I read a session trace?

Look at actions.requested / action.result pairs for the tool trajectory. Count calls by tool name before blaming latency. Separate host-side work (channel callTool, preparation) from tools the model chose.

turn.failed with "turn interrupted" means a follow-up or stop ended the turn on purpose.

If the model reads outside the session workspace, the prepared files don't match what the instructions expect. Fix the layout. See Hillclimbing.

agent-sdk trajectory --events <file> summarizes any saved NDJSON stream. The playground Open trace control does the same visually.

What's next