@cursor/sdk 1.0.22 — Local agent returns bare status=ERROR after idle; process restart fixes it (not quota)

Where does the bug appear (feature/product)?

Cursor SDK

Describe the Bug

We run a long-lived Node.js HTTP service that wraps @cursor/sdk for platform automation (Background Agents API, local runtime). After the process has been idle for a while (~15+ minutes), agent.send() fails with an opaque bare status=ERROR — the stream and run.wait() return no message, code, or error fields — in ~270 ms with zero streamed output (streamChars=0, events=2).

Restarting the host Node process immediately fixes it with the same API key and the same model: { id: "auto" }. This is not a usage/quota issue: Cursor.me({ apiKey }) succeeds before every failed send, and the same key works right after restart without any quota reset.

Sometimes the underlying cause leaks as process.on('unhandledRejection')ConnectError: [unauthenticated] Error, but it is not catchable in our send() / stream() / wait() chain.

Steps to Reproduce

  1. Install and use @cursor/[email protected] on Node.js 22.x (Windows 10).

  2. Create a local agent and verify the API key:

import { Agent, Cursor } from "@cursor/sdk";

await Cursor.me({ apiKey: process.env.CURSOR_API_KEY! }); // succeeds

const agent = await Agent.create({
  apiKey: process.env.CURSOR_API_KEY!,
  model: { id: "auto" },
  local: { cwd: "/path/to/repo" },
});
  1. Leave the Node process idle for ~15+ minutes (or reuse a cached agent handle after idle).

  2. Call:

const run = await agent.send("hello", { model: { id: "auto" } });

for await (const event of run.stream()) {
  if (event.type === "status" && event.status === "ERROR") {
    console.log("bare ERROR:", JSON.stringify(event));
  }
}

const result = await run.wait();
console.log(result);
  1. Observe instant failure (~270 ms, no assistant output).

  2. (Optional confirmation) Kill and restart the Node process, repeat step 4 with the same API key — run succeeds with normal streaming.

Minimal repro script (save as repro-bare-error.mjs):

import { Agent, Cursor } from "@cursor/sdk";

const apiKey = process.env.CURSOR_API_KEY!;
const cwd = process.cwd();

await Cursor.me({ apiKey });

const agent = await Agent.create({
  apiKey,
  model: { id: "auto" },
  local: { cwd },
});

console.log("Agent created:", agent.agentId);
console.log("Idle this process for 15+ minutes, then press Enter to send...");
await new Promise((resolve) => process.stdin.once("data", resolve));

const run = await agent.send("Say hello", { model: { id: "auto" } });
for await (const event of run.stream()) {
  console.log("stream:", JSON.stringify(event));
}
console.log("wait:", await run.wait());

Expected Behavior

  1. run.wait() should reject with a typed AuthenticationError (or return a result that includes code, message, and isRetryable: true).

  2. When the idle local-agent gRPC access token expires, the SDK should either transparently reconnect or surface a retryable error so host apps can agent.close() + Agent.resume(agentId) without restarting the whole process.

  3. Bare { status: "error" } with no payload should not be the only signal — it makes quota limits, transport lapses, and agent-busy states indistinguishable.

Operating System

Windows 10/11

Version Information

@cursor/sdk: 1.0.22
@cursor/sdk-win32-x64: 1.0.22
Node.js: 22.x
Host: Code Worker Agent (HTTP wrapper around Background Agents API, local runtime)
Integration: Agent.create() + agent.send() + run.stream() + run.wait()

For AI issues: which model did you use?

Model: { id: "auto" } (server-routed Auto)

Failures also occur when the resolved wait result shows "model":{"id":"default"}.

For AI issues: add Request ID with privacy disabled

Example request IDs from the 2026-07-06 incident (8 consecutive bare failures, all instant):

requestId=4edc81ca-6ad5-41c0-993a-3332e788ae20
requestId=5e800d87-67e4-45eb-8866-abe9790cf51f
requestId=9aa5a9c9-9084-4b12-8ede-4bbd2306b2c4
requestId=53628817-0c39-4a3b-ad73-ad2854a221b6
requestId=788bb2c1-d663-481e-830d-9731fb38d2e7

Example agent/run IDs from the same session:

agent_id=agent-84dcd591-2b7c-4d62-af8a-5b6d5005528e
run_id=run-ea041a38-9ad5-47f7-bfdd-69e6739fc1f6

Additional Information

On 2026-07-06:

  • Cursor Usage dashboard: API 100%, Auto 86% (misleading — restart fixed runs without quota reset).
  • 8 consecutive runs failed in ~90 seconds (08:52–08:53), all model=auto.
  • Manual Node process restart at 09:11 → success at 09:12, same key, same model.
  • Cursor.me({ apiKey }) succeeded on every attempt before send().

If this were true quota exhaustion, restarting the local process would not help.

Workaround (confirmed by Cursor staff in #161203 / #163819)

await agent.close?.();
const fresh = await Agent.resume(agentId, {
  apiKey: process.env.CURSOR_API_KEY!,
  model: { id: "auto" },
  local: { cwd: "/path/to/repo" },
});
// Retry send on `fresh` — usually succeeds immediately

Does this stop you from using Cursor

No - Cursor works, but with this issue

Hey, please update to the latest version. The host-process crash isn’t reproducible there anymore:

npm install @cursor/sdk@latest

The latest published version right now is 1.0.23. The unhandledRejection crash (ConnectError [unauthenticated]) was fixed back in 1.0.19, so on the current version the process shouldn’t crash.

One more thing. Surfacing this as a typed, catchable error on run.wait() so you get a structured error instead of a bare status: "error" is a separate improvement. It’s in progress, but it hasn’t made it into a published version yet, so I can’t share an exact version or timeline. Once it’s out in a published release, I’ll reply here.

If you still see the process crashing after upgrading, send the version and the stack trace and I’ll take a closer look.