Cursor-agent -p non-interactive not exiting at the end

Where does the bug appear (feature/product)?

Cursor CLI

Describe the Bug

When running cursor-agent in non-interactive mode it doesn’t exit at the end.

Steps to Reproduce

On Ubuntu 24.04, login into cursor-agent and have correct permissions.
Run: cursor-agent -p "Hi"

Expected Behavior

Agent should respond, then exit.

Operating System

Linux

Current Cursor Version (Menu → About Cursor → Copy)

cursor-agent version: 2025.09.04-fc40cd1

Does this stop you from using Cursor

Sometimes - I can sometimes use Cursor

Hey, thanks for the report, we’ll look into it.

Doesn’t work for me either on my MacBook Pro M1 (OS 14.1.2 (23B92))

For me using following script works:

#!/usr/bin/env python3
import argparse
import json
import re
import shutil
import subprocess
import sys
from typing import Any, Optional


def build_prompt(user_prompt: str) -> str:
    suffix = " When finished print: ===DONE==="
    return f"{user_prompt}{suffix}"


def run_cursor_agent(prompt: str, timeout_seconds: int = 120) -> subprocess.CompletedProcess:
    cmd = [
        "cursor-agent",
        "--print",
        "--output-format",
        "json",
        "--force",
        prompt,
    ]
    return subprocess.run(
        cmd,
        stdout=subprocess.PIPE,
        stderr=subprocess.PIPE,
        text=True,
        timeout=timeout_seconds,
        check=False,
    )


def parse_json_output(raw: str) -> Optional[Any]:
    try:
        return json.loads(raw)
    except json.JSONDecodeError:
        # Fallback: try to extract the largest JSON-looking slice
        first = raw.find("{")
        last = raw.rfind("}")
        if first != -1 and last != -1 and last > first:
            slice_ = raw[first : last + 1]
            try:
                return json.loads(slice_)
            except json.JSONDecodeError:
                return None
        return None


def extract_result_markdown(payload: Any) -> Optional[str]:
    if not isinstance(payload, dict):
        return None

    # User requested: prefer result.result
    result_obj = payload.get("result")
    if isinstance(result_obj, dict) and isinstance(result_obj.get("result"), str):
        return result_obj["result"]

    # Fallbacks commonly seen
    output_obj = payload.get("output")
    if isinstance(output_obj, dict) and isinstance(output_obj.get("result"), str):
        return output_obj["result"]

    if isinstance(result_obj, str):
        return result_obj

    return None


def strip_trailing_done_marker(text: str) -> str:
    # Remove a trailing ===DONE=== and surrounding whitespace/newlines at the very end
    return re.sub(r"\s*===DONE===\s*\Z", "", text, flags=re.MULTILINE)


def print_markdown(md: str) -> None:
    # Pretty render with rich if available; otherwise print raw
    try:
        from rich.console import Console
        from rich.markdown import Markdown

        Console().print(Markdown(md))
    except Exception:
        sys.stdout.write(md + ("\n" if not md.endswith("\n") else ""))


def main() -> int:
    parser = argparse.ArgumentParser(description="Run cursor-agent and pretty print result.result as Markdown.")
    parser.add_argument("prompt", nargs=argparse.REMAINDER, help="Prompt to send to cursor-agent")
    parser.add_argument("--timeout", type=int, default=120, help="Timeout in seconds (default: 120)")
    args = parser.parse_args()

    if not args.prompt:
        sys.stderr.write("Usage: cursor-agent-run.py <your prompt>\n")
        return 2

    user_prompt = " ".join(args.prompt).strip()
    real_prompt = build_prompt(user_prompt)

    # Ensure cursor-agent exists
    if not shutil.which("cursor-agent"):
        sys.stderr.write("cursor-agent not found in PATH\n")
        return 127

    try:
        proc = run_cursor_agent(real_prompt, timeout_seconds=args.timeout)
    except subprocess.TimeoutExpired:
        sys.stderr.write("cursor-agent timed out\n")
        return 124

    if proc.returncode != 0 and not proc.stdout:
        sys.stderr.write(proc.stderr or "cursor-agent failed\n")
        return proc.returncode or 1

    data = parse_json_output(proc.stdout)
    if data is None:
        sys.stderr.write("Failed to parse JSON from cursor-agent output\n")
        # Show raw output to aid debugging
        sys.stdout.write(proc.stdout)
        return 1

    md = extract_result_markdown(data)
    if md is None:
        # Fallback: show the entire JSON payload prettified
        sys.stdout.write(json.dumps(data, indent=2, ensure_ascii=False) + "\n")
        return 0

    md = strip_trailing_done_marker(md)
    print_markdown(md)
    return 0


if __name__ == "__main__":
    raise SystemExit(main())


I did more tests and there are folders in which the “-p” exits and some in which it hangs:

  • HANG in a folder containing a clone of GitHub - lbke/mic2key
  • HANG in a copy of the folder above
  • EXIT in the parent folder where I did the clone
  • HANG in my home folder
  • HANG in a new folder I create
  • EXIT in a folder in which I create the script mentioned by @eeeie above.
  • EXIT in my home folder (why ???)

I don’t see a pattern, and repeat runs are not always consistent.

Where does the bug appear (feature/product)?

Cursor CLI

Describe the Bug

After running the Cursor Agent, once the process finishes, the command does not exit as expected.

Steps to Reproduce

cursor-agent -p --force --output-format text

also i use api key

Expected Behavior

exit command with 0 code

Screenshots / Screen Recordings

Operating System

Linux

Current Cursor Version (Menu → About Cursor → Copy)

cli version: 2025.09.18-7ae6800

For AI issues: which model did you use?

sonnet-4

Hello, we’re facing a similar issue in our GitHub action that’s based on the official Code Review Cookbook. The step `Perform automated code review` does not emit any exit code causing the action to hang in the step indefinitely (even though the agent has long finished the review).

2 Likes

I’ve run into the same issue — the command doesn’t exit with code 0, but instead the step just hangs and only finishes after 30 minutes. The only workaround so far is setting a timeout. Is there a proper solution to this problem?

1 Like

Bumping the topic

This topic was automatically closed 22 days after the last reply. New replies are no longer allowed.