Where does the bug appear (feature/product)?
Cursor SDK
Describe the Bug
The SDK’s exception hierarchy groups errors by incorrect parent classes. Three pairs are affected:
1. PermissionDeniedError(403) inherits AuthenticationError(401)
errors.py line 65: class PermissionDeniedError(AuthenticationError)
This means catching AuthenticationError to handle bad API keys also catches 403 Forbidden errors. A user who typed their key wrong gets the same error-handling code path as someone whose key is valid but lacks access to a resource. Different HTTP concepts, same except block.
2. InternalServerError(500) inherits NetworkError
errors.py line 107: class InternalServerError(NetworkError)
This is the dangerous one. Any retry-on-network-error logic will silently retry 500s. A server crash gets the same treatment as a dropped connection. If you have retry logic with backoff, a 500 from the Cursor API will be retried repeatedly instead of surfaced immediately.
3. BadRequestError(400) inherits ConfigurationError
errors.py line 81: class BadRequestError(ConfigurationError)
Catching ConfigurationError (e.g. “you forgot to set CURSOR_API_KEY”) also catches 400 Bad Request errors (e.g. “your prompt is too long”). These need different user-facing messages.
Steps to Reproduce
from cursor_sdk.errors import (
AuthenticationError, PermissionDeniedError,
NetworkError, InternalServerError,
ConfigurationError, BadRequestError,
)
403 should NOT inherit from 401
print(“PermissionDeniedError extends AuthenticationError:”,
issubclass(PermissionDeniedError, AuthenticationError))
→ True (should be False)
500 should NOT inherit from network error
print(“InternalServerError extends NetworkError:”,
issubclass(InternalServerError, NetworkError))
→ True (should be False)
400 should NOT inherit from config error
print(“BadRequestError extends ConfigurationError:”,
issubclass(BadRequestError, ConfigurationError))
→ True (should be False)
Expected Behavior
CursorSDKError
├── AuthenticationError # 401
├── PermissionDeniedError # 403 (NOT under AuthenticationError)
├── NetworkError # connection failures, timeouts
│ └── APITimeoutError
├── InternalServerError # 500 (NOT under NetworkError)
├── ConfigurationError # missing API key, bad config
├── BadRequestError # 400 (NOT under ConfigurationError)
├── RateLimitError # 429
└── …
Operating System
Windows 10/11
Version Information
N/A cursor-SDK v0.1.6
Additional Information
Enviornment: Python 3.11
Workaround:
Catch leaf types before parent types. Always import and handle the specific errors (PermissionDeniedError, InternalServerError, BadRequestError) BEFORE their incorrectly-assigned parents:
from cursor_sdk import Agent, AgentOptions, LocalAgentOptions
from cursor_sdk.errors import (
PermissionDeniedError, AuthenticationError,
InternalServerError, APITimeoutError, NetworkError,
BadRequestError, ConfigurationError,
)
Auth block: leaf (403) before parent (401)
try:
agent = Agent.create(model=“gpt-5”, local=LocalAgentOptions(cwd=“.”))
except PermissionDeniedError:
print(“Access denied — you don’t have permission for this resource”)
except AuthenticationError:
print(“Bad API key — check CURSOR_API_KEY”)
Dispatch block: leaf (500) before parent (NetworkError)
try:
result = agent.prompt(“do something”)
except InternalServerError:
raise # 500 — don’t retry, surface immediately
except APITimeoutError:
retry() # timeout — safe to retry
except NetworkError:
retry() # connection failure — safe to retry
Config block: leaf (400) before parent (ConfigurationError)
except BadRequestError:
print(“Invalid request — check your prompt or parameters”)
except ConfigurationError:
print(“Configuration issue — check your setup”)
Thank you for shipping the SDK, hope my report is useful:)
Does this stop you from using Cursor
No - Cursor works, but with this issue