Fix common agent problems
Start with three checks:
- Run
bdk validate --dir .. - Confirm the expected agent and connections in
bdk info --dir . --json. - Reproduce once, then inspect the serve log or session trace.
Match the visible symptom below.
Serve or the playground will not work
| What you see | What to do |
|---|---|
serve exits before listening | Fix the errors from bdk validate --dir ., then start it again. |
| Playground is blank or says there are no agents | Start bdk serve; built playground assets have no agent data without the server. |
| Agent-file edits do not appear | Open the URL printed by serve --dev, then press Enter in its TTY to reload after file changes. |
| Sessions exist but the list is empty | The list is scoped to the authenticated principal. Use the same identity that created the session; a ?sessionId= deep link does not bypass authorization. |
| The preferred port is busy | Use the next port printed by the CLI, pass --port <number>, or pass --port 0 for any free port. |
A model turn fails
| What you see | What to do |
|---|---|
File reads and greps fail repeatedly with NGHTTP2_FRAME_SIZE_ERROR | Run the agent under Node 22.13 or newer, never Bun. |
| The turn immediately reports a missing API key | Set CURSOR_API_KEY, CURSOR_API_KEY_FILE, or CURSOR_SERVICE_ACCOUNT_KEY, or run bdk login. |
| Login succeeds but the model host rejects the key | Point login and model traffic at the same API host; check CURSOR_API_BASE_URL and CURSOR_BACKEND_URL. |
The first turn reports [resource_exhausted] | Treat it as a capacity-shaped error. For a GitHub wake, also check the connected GitHub credential and any GitHub 429 rate limit before assuming the model quota is exhausted. |
A turn behaves unexpectedly
| What you see | What to do |
|---|---|
Replies quote an ancestor AGENTS.md or unrelated repository rules | Inspect local.cwd in /v1/info. Set defineAgent({ local: { cwd } }) or the session workspace explicitly instead of inheriting the parent checkout. |
| The model lists IDE meta-tools but never calls named MCP tools | Set advertiseTools: true on the local model-visible connection and confirm it under connections in /v1/info. |
| Server tools, skills, or seed files are absent | Check the discovered project in /v1/info; follow the capability warning from validate. |
validate and run pass but CI type-checking fails | Run the project type check. Tool results must be JSON-shaped; prefer object literals or type aliases over interface return types. |
HTTP returns 401, 403, or 409
| What you see | What to do |
|---|---|
401 outside local development | Send the configured bearer token, alias token, or channel credential. Default custom routes admit direct loopback only. |
403 on a stream or follow-up | Use the same authenticated principal that created the session. |
| A custom channel route fails through a tunnel or LAN | Protect the shared host with --bearer-token or authored channel auth. Use --allow-anonymous only behind a proxy that authenticates every caller. |
409 on a follow-up | Refresh the rotating continuationToken and confirm the target is a chat session; task sessions do not accept follow-ups. |
409 session_busy from call --session | Wait for the turn, omit --session, or mark a read-only tool with effect: "read" so it can run concurrently. |
| A custom route fails schema type-checking | Add the required Zod querySchema for GET, or bodySchema for POST, PUT, and PATCH. See Channels. |
A hosted deployment fails or looks stale
| What you see | What to do |
|---|---|
validate or deploy reports architecture "v2" requires a named secret | A githubChannel credential or a GitLab or Bitbucket webhookSecret is a literal or a getter. Replace it with the quoted { secretName: "NAME" } and set the value with bdk secrets set <slug> NAME. See Other hosts. |
| Local changes do not appear after deploy | deploy reads a pushed Git ref, not the working tree. Commit and push the intended ref, then deploy it again. |
| The deployment never becomes running | Run bdk deployment <slug>, then act on the reported build or startup error. |
| A secret or egress change has no effect | Confirm the name with secrets list, verify the hosting block exists on the pushed ref, and follow the CLI or dashboard prompt to redeploy. |
| Cursor events stopped after redeploy | Each redeploy replaces the watched repositories. Pass every --cursor-events-repo value again; without the flag, the deployment watches only its GitHub source repository. |
run --prod prints a deliveryId but no sessionId | Expected, with or without --dry-run: the session is created when the turn starts. Find it with bdk logs --prod --once --slug <slug>, then follow Verify a hosted turn. |
session <sessionId> --prod shows a running run with no logs | Runs lag behind events, and a high-effort turn can take several minutes. Rerun the command; a growing event count means the turn is still working. |
| A dashboard link to a single session doesn't open | Check the session with bdk session <sessionId> --prod --slug <slug>. |
chat, call, sessions, cost, or mcp with --prod says the command is not supported | Hosted deployments accept --prod only on run, session, logs, and playground. See Verify a hosted turn. |
run --prod --session <sessionId> reports the session belongs to another channel | --session continues only sessions that run --prod or session --prod started. Reach Slack, SCM, and custom-route sessions through their own channel. |
A custom route returns 202 with no body | Expected on managed hosting. The handler runs after the response, so deliver results from the channel. See Route responses. |
| External HTTP rejects the alias token | Update the caller with the current token. If the token is lost or exposed, run bdk rotate-token <slug> and replace it everywhere. |
deploy, a hosted session, or an eval run says it is not available with Privacy Mode (Legacy) | Switch to Privacy Mode, or ask a team admin if your team enforces this setting. A refused session does not store the message. See Privacy Mode. |
deploy says persistent deployments are limited to 1 per team | Another deployment of the team already sets persistent: true. Remove the setting from one agent, or delete that deployment. |
| A schedule does not fire on the hosted deployment | Make sure the deployment is running and its release has the schedule. Deploy again with @cursor/bdk 0.2.14 or newer. |
A request to a custom route gets 429 | The deployment received more requests than its limit. Wait for the time in Retry-After, then send the request again. |
See Deployment for source, credential, and redeployment boundaries.
GitHub deliveries fail
| What you see | What to do |
|---|---|
github forward returns 401 on every delivery after creating the hook | Clear GITHUB_TOKEN and GH_TOKEN for that command so the forwarder uses the gh CLI login: GITHUB_TOKEN= GH_TOKEN= bdk github forward .... |
Hook already exists | Run one forwarder for the repository and stop the stale process. |
Direct webhook deliveries are rejected outside --dev | Configure the same GITHUB_WEBHOOK_SECRET on the signer and server. Cursor event pull needs no public webhook or webhook secret. |
| You cannot create a forwarder | Use bdk github replay <pr-url>; it needs pull access instead of repository administration. |
The GitHub guide covers event pull, replay, and direct webhooks.
Slack does not respond
| What you see | What to do |
|---|---|
Logs say channel idle ... missing credentials | Run bdk slack create --dir <agent>, then check the generated prefix with bdk slack doctor. Use slack init --manual only for an app you own. |
slack create is waiting for workspace approval | Keep the CLI running, open the Request approval link it prints, and select Retry in the dashboard's Add Slack wizard after an admin approves. See Slack. |
| The bot ignores ordinary channel posts | Configure engagement.channelPosts, create the app with --channel-posts, and invite it to every allowed channel. |
See the Slack guide for app setup and engagement choices.
MCP authorization fails
| What you see | What to do |
|---|---|
must be defineConnection({ url, oauth: true }) | Add oauth: true to the named URL connection or correct the connection filename passed to mcp oauth. |
| Local auth works but hosted calls return unauthorized | Locally stored OAuth tokens don't reach a deployment. Use a cursorAccount: true connection and connect the server from Deployed Agents in the dashboard. See Host MCP OAuth. |
The model invents mcp_auth instead of calling the server | Set advertiseTools: true for named local tools, or expose a host-side wrapper through ctx.host.mcp. |
See Host MCP OAuth for the credential lifecycle.
Schedules or reminders stall
| What you see | What to do |
|---|---|
A schedule or reminder never fires under --dev | Trigger it through the matching /v1/dev/schedules/<id> or /v1/dev/reminders/<id> route; development mode does not auto-fire. |
A reminder is disarmed with handler_lost_on_restart | Recreate handler-form reminders after restart, or use a prompt-form reminder. |
| The same schedule fires on several self-hosted processes | Enable schedules on one serving process per project and pass --no-schedules to the others. |
A credential was exposed
| What you see | What to do |
|---|---|
| A secret value appears in a command, transcript, or shell history | Rotate it at the provider, set it again through hidden input or stdin, and test the feature. |
| An alias token appears outside its secure destination | Run bdk rotate-token <slug>, store the new value outside transcripts, and update every caller. |
| Someone printed a secret to verify it | Rotate it. Confirm only the name with bdk secrets list <slug>, then redeploy and exercise the feature. |
Read the session trace
Start with actions.requested and action.result. Count calls by tool
name, pair every request with its result, and separate host preparation
from tools chosen by the model.
turn.failed with turn interrupted usually means a follow-up or stop
ended the turn deliberately. Other failures should have an error event
near the tool or model boundary that produced them.
Use the trace path printed by bdk run:
bdk trajectory --events <trace-path>The playground's Open trace control renders the same trajectory. When the agent runs but makes poor decisions, move from diagnosis to a measured hillclimb.
Related
- Deployment: hosted and self-hosted boundaries
- Sessions: session ownership and event vocabulary
- CLI reference: command flags and exit behavior