Skip to content

[Bug]: Cursor thread fails to start with ProviderAdapterSessionClosedError when project .cursor/cli.json contains deprecated keys — detect and offer auto-migration #12451

Description

@jzelez

Before submitting

  • I searched existing issues and did not find a duplicate.
  • I included enough detail to reproduce or investigate the problem.

Area

apps/server

Steps to reproduce

  1. In a project directory, have a .cursor/cli.json containing keys that were valid for older cursor-agent versions but are rejected by the current one, e.g.:
    {
    "approvalMode": "unrestricted",
    "sandbox": { "mode": "disabled", "networkAccess": "allow_all" },
    "permissions": { "allow": ["Shell(**)"] }
    }
  2. Open that project as a thread workspace in T3 Code.
  3. Set the thread's provider to Cursor and send a prompt.
  4. The turn fails immediately.

Expected behavior

Two layers of grace:

  1. If the cursor-agent subprocess fails at startup, T3 Code should capture its stderr and surface it. cursor-agent prints a precise, actionable schema error before exiting ("Invalid project config at .../.cursor/cli.json: Unrecognized key(s): 'approvalMode', 'sandbox'") — the user should see that, not "ACP process exited with code 1".

  2. Better still: T3 Code already knows the workspace root and spawns the provider — it's in a position to parse .cursor/cli.json itself (or read the provider's stderr), recognize known deprecated/invalid keys, and offer a one-click "auto-fix" for known migration paths. In this concrete case the current cursor-agent moves these settings to sibling files the CLI already reads:

    • "approvalMode" (and autoRun) → .cursor/permissions.json
    • "sandbox" → .cursor/sandbox.json (e.g. {"type": "insecure_none"})
      A prompt like "Deprecated keys found in .cursor/cli.json — migrate to current format?" would turn a hard, inscrutable failure into a 5-second fix, and would generalize to future cursor-agent schema changes (and other ACP providers with similar config drift).

Actual behavior

T3 Code spawns cursor-agent acp with cwd = the project directory. cursor-agent validates the project config on startup, finds unrecognized keys, prints the validation error to stderr, and exits 1. The adapter reports only:

ProviderAdapterSessionClosedError: cursor adapter thread is closed:
[cause]: AcpProcessExitedError: ACP process exited with code 1

The stderr is dropped, so the actionable message never reaches the user. Reproduced from a shell: cursor-agent --version inside the project exits 1 with the schema error; from any other directory it exits 0 (cursor-agent 2026.09.15-d2fe57e). Net effect: a routine cursor-agent auto-update silently bricks every Cursor thread in the workspace, with an error message that points at T3 Code rather than the config file.

Impact

Major degradation or frequent failure

Version or commit

T3 Code (Alpha) desktop 0.0.42

Environment

macOS 27.0 (26A428), Apple Silicon T3 Code (Alpha) 0.0.42 desktop Provider: Cursor (ACP), cursor-agent 2026.09.15-d2fe57e at ~/.local/share/cursor-agent/

Logs or stack traces

ProviderAdapterSessionClosedError: cursor adapter thread is closed: 45515176-6c8b-414b-9aba-4617c2e2703c
    at mapAcpToAdapterError (.../apps/server/dist/bin.mjs:145221:45)
    at startSession (.../apps/server/dist/bin.mjs:203454:59)
    at ensureSessionForThread (.../apps/server/dist/bin.mjs:203373:33)
    at processTurnStartRequested (.../apps/server/dist/bin.mjs:203752:36)
    at processDomainEvent (.../apps/server/dist/bin.mjs:204127:46) {
  [cause]: AcpProcessExitedError: ACP process exited with code 1
      at Object.onSuccess (.../apps/server/dist/bin.mjs:108047:23)
}

Underlying process stderr (recovered by running cursor-agent manually in the same cwd):
Invalid project config at /Users/<user>/Projects/<repo>/.cursor/cli.json: schema validation failed. [
  {
    "code": "unrecognized_keys",
    "keys": ["approvalMode", "sandbox"],
    "path": [],
    "message": "Unrecognized key(s) in object: 'approvalMode', 'sandbox'"
  }
]

Screenshots, recordings, or supporting files

No response

Workaround

Manually remove the rejected keys from the project's .cursor/cli.json; move approval/sandbox settings into .cursor/permissions.json and .cursor/sandbox.json respectively. Cursor turns start working immediately. (This manual fix is exactly what the proposed auto-migration prompt should do for the user.)

Activity

  1. added
    bugSomething is broken or behaving incorrectly.
    needs-triageIssue needs maintainer review and initial categorization.
    on Sep 18, 2026
  2. juliusmarminge commented on Sep 18, 2026

    @juliusmarminge
    Member

    Triage

    Confirmed on current main (9ea9c3d5d). Still present vs the reporter’s desktop 0.0.42. Real Cursor ACP startup bug: the child prints an actionable schema error and exits 1; T3 drains that stderr and surfaces only ACP process exited with code 1. Not a duplicate of #4380 / #12224, #5943, or #10480. No open PR fixes this.

    What happens

    A turn starts ensureSessionForThread → ProviderService.startSession → Cursor adapter makeCursorAcpRuntime → acp.start(). Spawn is cursor-agent acp with cwd = the thread workspace, so current cursor-agent reads .cursor/cli.json before ACP initialize. On unrecognized keys it writes the schema error to stderr and exits 1.

    makeCursorAcpRuntime never sets onStderr. The shared ACP runtime still reads the pipe (so it does not back-pressure) and then throws the text away. effect-acp also drains child stderr at the stdio layer and builds the exit error with only code / pid. AcpProcessExitedError has no stderr field. mapAcpToAdapterError then wraps that exit as a session-closed error whose own message is only the thread id.

    That matches the pasted stack (processTurnStartRequested → ensureSessionForThread → startSession → mapAcpToAdapterError). formatFailureDetail pretty-prints that cause chain and never had a stderr excerpt to include. T3 does not read .cursor/cli.json anywhere.

    The reporter’s manual repro is the right one: cursor-agent --version in the project cwd exits 1 with the schema text; the same binary outside that cwd is fine. Workaround (move approvalMode / sandbox out of cli.json) is correct and is a user-side config migration, not a T3 fix.

    Not a duplicate

    Closest open work is #9839 (Antigravity-only): it adds bounded startup-diagnostic plumbing on AcpSessionRuntime and wires it for Antigravity install validation. It does not attach stderr to Cursor / Grok AcpProcessExitedError, and it does not change mapAcpToAdapterError. #12337 recycles Cursor sessions after a transport failure on an already-started session. Neither is a fix here.

    Fix direction (must-fix first)

    1. Keep a bounded, redacted tail of ACP child stderr (last few KiB; same secret-hygiene as Antigravity — no tokens, pairing URLs, or home paths).
    2. On early process death, wait briefly so that tail is available, then put a sanitized excerpt on AcpProcessExitedError (or a dedicated startup-failure error).
    3. Map that to a user-facing adapter error whose detail is the excerpt, not cursor adapter thread is closed: <uuid>. Do this for every ACP driver (Cursor and Grok share the hole today; Antigravity is the only one with an onStderr hook, and that hook is for auth URLs).
    4. Prove it with a fake cursor-agent that writes the reporter’s unrecognized_keys payload and exits 1 before ACP initialize. Assert the activity / lastError text contains cli.json / Unrecognized key and not only exited with code 1.

    Out of scope for the first PR

    Auto-parsing .cursor/cli.json and offering a one-click migrate to .cursor/permissions.json / .cursor/sandbox.json. T3 should not rewrite project Cursor config without an explicit product decision; that schema will keep moving. Surfacing the native stderr turns this into a 5-second manual fix (the reporter’s workaround) for every current and future cursor-agent rejection.

    Workaround until then: remove the rejected keys from the project .cursor/cli.json (move approval/sandbox into the sibling files the current CLI reads). Cursor turns start immediately.

  3. added
    acceptedfeature request accepted
    via-triageFiled through npx t3 triage
    and removed
    needs-triageIssue needs maintainer review and initial categorization.
    on Sep 18, 2026
  4. cestercian commented on Sep 19, 2026

    @cestercian
    Contributor

    Taking this — will detect deprecated .cursor/cli.json keys on Cursor ACP startup and surface/migrate instead of a opaque ProviderAdapterSessionClosedError.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    acceptedfeature request acceptedbugSomething is broken or behaving incorrectly.via-triageFiled through npx t3 triage

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions