Skip to content

Restart bridge on failure - #5

Merged
RawToast merged 3 commits into
masterfrom
restart-bridge
Jul 20, 2026
Merged

Restart bridge on failure#5
RawToast merged 3 commits into
masterfrom
restart-bridge

Conversation

@RawToast

@RawToast RawToast commented Jul 20, 2026

Copy link
Copy Markdown
Owner

https://forum.cursor.com/t/cursor-sdk-1-0-22-local-agent-returns-bare-status-error-after-idle-process-restart-fixes-it-not-quota/164866

What this PR does

This PR adds recovery logic for the Cursor SDK local-agent 'bare status=error' stuck state. It classifies opaque retryable SDK errors and stale exchanged-token auth failures, preserves the last agent ID so subsequent requests try Agent.resume before falling back to Agent.create, and triggers a configurable bridge process restart after repeated opaque failures. The server now waits for the bridge /health endpoint to be healthy before considering a restart successful.

Summary by CodeRabbit

  • Bug Fixes
    • Improved recovery from local agent timeouts and transient SDK failures by evicting cached agents while preserving resume state.
    • Enhanced agent caching to resume prior in-progress work when possible, with fallback to fresh agents after opaque failures.
    • Improved SDK failure classification so stale authentication/token-like issues are treated as retryable, while other auth/capacity errors are handled separately.
    • Improved bridge restart logic by waiting for the bridge to become healthy before continuing.
  • Tests
    • Added coverage for opaque SDK failures and stale auth retryable behavior, including error-shape variations.

@kanri-san

kanri-san Bot commented Jul 20, 2026

Copy link
Copy Markdown

Review failed

KANRI_PI_MODEL did not match an available model: opencode-go/kimi-k3


Walkthrough by kanri

@coderabbitai

coderabbitai Bot commented Jul 20, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

The bridge classifies opaque and stale SDK failures, preserves agent IDs for resume attempts, handles timeout recovery, schedules guarded restarts, and exposes classifiers for tests. server.ts waits for /health after respawning the bridge.

Changes

Opaque SDK failure recovery

Layer / File(s) Summary
Failure classification and restart scheduling
scripts/cursor-sdk-local-agent-bridge.mjs, scripts/__tests__/cursor-sdk-local-agent-bridge.test.mjs
Adds opaque and stale-auth classification, structured logging, consecutive-failure tracking, guarded restart scheduling, and tests for opaque, capacity, authorization, and stale-auth errors.
Agent timeout and resumption flow
scripts/cursor-sdk-local-agent-bridge.mjs
Updates timeout cleanup, cached-agent eviction, resume-ID tracking, and agent creation to resume prior agents or fall back to creation.
Restarted bridge health supervision
server.ts
Respawns the bridge after unexpected exit, reattaches supervision, waits for /health, and logs health success or failure.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant AgentSDK
  participant LocalBridge
  participant AgentCache
  participant BridgeProcess
  participant Server
  participant HealthEndpoint
  AgentSDK-->>LocalBridge: opaque or stale run failure
  LocalBridge->>AgentCache: evict and preserve agent ID
  LocalBridge->>AgentSDK: resume agent or create replacement
  LocalBridge->>LocalBridge: count failures
  LocalBridge->>BridgeProcess: close and exit at threshold
  BridgeProcess-->>Server: unexpected exit
  Server->>BridgeProcess: respawn after delay
  Server->>HealthEndpoint: wait for /health
  HealthEndpoint-->>Server: health result
Loading
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title is concise and accurately reflects the main change: restarting the bridge when it fails.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch restart-bridge

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (4)
scripts/cursor-sdk-local-agent-bridge.mjs (3)

2691-2701: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Counter only resets on auth errors, not on other non-opaque failures.

consecutiveOpaqueFailures accumulates across any opaque failure and is only reset by noteSdkRunSuccess() or an authentication error. A retryable-but-not-opaque failure (e.g. capacity/rate-limit) in between two opaque failures doesn't reset the count, so genuinely non-consecutive opaque incidents can still add up toward the restart threshold. Low impact with the default threshold of 1, but undermines the "consecutive" semantics if CURSOR_SDK_BRIDGE_OPAQUE_FAILURE_RESTART is raised.

♻️ Proposed fix
 function noteSdkRunFailure(error) {
   if (isOpaqueSDKRunFailure(error)) {
     consecutiveOpaqueFailures += 1
     if (consecutiveOpaqueFailures < opaqueFailureRestartThreshold) return
     scheduleBridgeRestart(...)
     return
   }
-  if (isAuthenticationSDKError(error)) consecutiveOpaqueFailures = 0
+  consecutiveOpaqueFailures = 0
 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@scripts/cursor-sdk-local-agent-bridge.mjs` around lines 2691 - 2701, Update
noteSdkRunFailure so every non-opaque SDK failure resets
consecutiveOpaqueFailures, while preserving the existing opaque-failure
increment and restart logic. Keep authentication handling consistent with this
reset behavior so only uninterrupted opaque failures contribute toward
opaqueFailureRestartThreshold.

2675-2711: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

No test coverage for noteSdkRunFailure/scheduleBridgeRestart threshold and restart-trigger logic.

Only isOpaqueSDKRunFailure is unit tested. The threshold accumulation, single-shot bridgeRestartScheduled guard, and reset-on-success/auth behavior in noteSdkRunFailure/scheduleBridgeRestart are core to this PR's objective and currently untested.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@scripts/cursor-sdk-local-agent-bridge.mjs` around lines 2675 - 2711, Add
tests covering noteSdkRunFailure and scheduleBridgeRestart: verify opaque
failures accumulate without restarting below opaqueFailureRestartThreshold,
trigger exactly one restart at the threshold despite subsequent failures, and do
not schedule outside the main module. Also verify noteSdkRunSuccess and
authentication failures reset consecutiveOpaqueFailures, while preserving
existing non-opaque failure behavior.

2703-2711: 🧹 Nitpick | 🔵 Trivial

Restart blast radius: any consecutive-opaque threshold hit kills all cached agents, not just the failing one.

closeAndExit(1) tears down the whole bridge process, so every other in-flight/cached agent session (unrelated cacheKeys) is dropped along with the one that actually failed. With the default threshold of 1, a single opaque failure on any session restarts the bridge for everyone. This is likely intentional per the PR's goal, but worth confirming this trade-off is acceptable for concurrent multi-session usage, and consider logging a metric/count for how often this fires in practice.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@scripts/cursor-sdk-local-agent-bridge.mjs` around lines 2703 - 2711, Update
scheduleBridgeRestart and its callers to avoid terminating the entire bridge
when a consecutive-opaque threshold is reached for one cacheKey; isolate
recovery to the failing agent session while preserving normal restart behavior
for unrecoverable bridge-wide failures. If bridge-wide termination remains
required, add a metric or counter and include it in the existing restart logging
to quantify these events.
scripts/__tests__/cursor-sdk-local-agent-bridge.test.mjs (1)

67-96: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Good coverage of the three main branches; consider adding the remaining classifier branches.

Not exercised: the error.code && error.code !== "cursor_sdk_error" early-return, and the case where summary.code is present while message/status don't otherwise disqualify it. Since this classifier gates bridge restarts, a bit more branch coverage would add confidence.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@scripts/__tests__/cursor-sdk-local-agent-bridge.test.mjs` around lines 67 -
96, The classifier tests around isOpaqueSDKRunFailure should cover the remaining
branches: add an error with a non-cursor_sdk_error code to verify the early
false return, and add an otherwise-eligible error whose summary.code is
populated while message and status do not disqualify it to verify the expected
classification.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@scripts/cursor-sdk-local-agent-bridge.mjs`:
- Around line 253-269: Update the final non-retryable error path in the run
catch handling to evict the cached agent before noteSdkRunFailure(error) and
rethrowing. Apply this when shouldRetry is false, preserving the existing resume
behavior based on isOpaqueSDKRunFailure(error), so exhausted retries or
already-emitted events cannot leave a broken agent in agentCache.

---

Nitpick comments:
In `@scripts/__tests__/cursor-sdk-local-agent-bridge.test.mjs`:
- Around line 67-96: The classifier tests around isOpaqueSDKRunFailure should
cover the remaining branches: add an error with a non-cursor_sdk_error code to
verify the early false return, and add an otherwise-eligible error whose
summary.code is populated while message and status do not disqualify it to
verify the expected classification.

In `@scripts/cursor-sdk-local-agent-bridge.mjs`:
- Around line 2691-2701: Update noteSdkRunFailure so every non-opaque SDK
failure resets consecutiveOpaqueFailures, while preserving the existing
opaque-failure increment and restart logic. Keep authentication handling
consistent with this reset behavior so only uninterrupted opaque failures
contribute toward opaqueFailureRestartThreshold.
- Around line 2675-2711: Add tests covering noteSdkRunFailure and
scheduleBridgeRestart: verify opaque failures accumulate without restarting
below opaqueFailureRestartThreshold, trigger exactly one restart at the
threshold despite subsequent failures, and do not schedule outside the main
module. Also verify noteSdkRunSuccess and authentication failures reset
consecutiveOpaqueFailures, while preserving existing non-opaque failure
behavior.
- Around line 2703-2711: Update scheduleBridgeRestart and its callers to avoid
terminating the entire bridge when a consecutive-opaque threshold is reached for
one cacheKey; isolate recovery to the failing agent session while preserving
normal restart behavior for unrecoverable bridge-wide failures. If bridge-wide
termination remains required, add a metric or counter and include it in the
existing restart logging to quantify these events.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: a262da6d-34d9-4f61-938e-ce1c64f8f7f9

📥 Commits

Reviewing files that changed from the base of the PR and between f135ed8 and 1290889.

📒 Files selected for processing (3)
  • scripts/__tests__/cursor-sdk-local-agent-bridge.test.mjs
  • scripts/cursor-sdk-local-agent-bridge.mjs
  • server.ts

Comment thread scripts/cursor-sdk-local-agent-bridge.mjs Outdated
RawToast added 2 commits July 20, 2026 15:42
Format stale-auth tests for oxfmt and reset opaque-failure counters on any
non-opaque error so exhausted retries cannot reuse broken cached agents.
@RawToast
RawToast merged commit c2b220f into master Jul 20, 2026
2 checks passed
@RawToast
RawToast deleted the restart-bridge branch July 20, 2026 11:49
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant