Handling Authentication Errors with the Cursor SDK: 401s, Team vs User Keys
The Cursor SDK validates the CURSOR_API_KEY environment variable at startup, throwing distinct errors for missing credentials versus invalid tokens, while HTTP 401 responses indicate authentication failures and 403 responses signal permission mismatches between team and user keys.
The cursor/plugins repository provides an SDK that authenticates via API keys to authorize plugin operations. Understanding how to handle Cursor SDK authentication errors—including the distinction between team keys for shared resources and user keys for individual identity—is essential for building robust plugins that fail gracefully when credentials are missing or insufficient.
Token Types: Team Keys vs User Keys
The Cursor SDK supports two distinct authentication patterns that determine the scope and audit trail of API operations.
Team keys are created for organizations in the Cursor console. These keys authorize any member of the team to act on behalf of shared resources. Use team keys when a plugin runs as a shared entity, such as a CI/CD bot posting to a centralized Slack channel.
User keys are generated for individual accounts. These keys authorize only the specific user's identity and permissions. Use user keys when a plugin must act as a specific person, such as posting a comment that appears to come from the author.
Both token types populate the same CURSOR_API_KEY environment variable, but they differ in scope, auditing, and permission boundaries according to the cursor-sdk/skills/cursor-sdk/references/auth.md documentation.
Early Validation and Error Messages
The SDK enforces authentication requirements immediately upon initialization in orchestrator entry points.
When CURSOR_API_KEY is undefined, the CLI scripts in orchestrate/skills/orchestrate/scripts/cli/task.ts (line 136), inspect.ts (line 167), and forensics.ts (line 96) abort execution with the error:
CURSOR_API_KEY required; see cursor-sdk/references/auth.md
The model-probing helper at orchestrate/skills/orchestrate/scripts/tools/probe-models.ts (line 94) logs a shorter variant:
CURSOR_API_KEY missing
This fail-fast approach in orchestrate/skills/orchestrate/scripts/core/agent-manager.ts (line 228) prevents the SDK from attempting API calls without valid credentials.
Handling HTTP 401 and 403 Responses
When a key is present but invalid or under-scoped, the Cursor SDK returns standard HTTP status codes that surface as JavaScript errors.
401 Unauthorized indicates an invalid token. The SDK throws an AuthenticationError or a generic Error containing "401" or "Invalid token". This occurs when the API key is malformed, expired, or revoked.
403 Forbidden indicates a permission mismatch. This error appears when authenticating with a user key that lacks access to team-level resources, or when the token's scope does not cover the requested operation.
The agent-manager.ts file (lines 862-907) demonstrates where the SDK interprets these HTTP statuses, converting them into actionable error messages before surfacing them to the user.
Implementing Robust Error Handling
Minimal Environment Check
Mirror the validation pattern found in the CLI entry points to fail fast when the environment variable is absent:
// my-plugin/src/main.ts
const apiKey = process.env.CURSOR_API_KEY;
if (!apiKey) {
console.error(
"CURSOR_API_KEY required; see cursor-sdk/references/auth.md"
);
process.exit(1);
}
This guard matches the implementation in task.ts (line 136) and prevents undefined tokens from reaching the SDK client.
Differentiating Permission Errors
Inspect error status codes to provide specific guidance for credential issues:
import { Cursor } from "cursor-sdk";
async function runWithAuth() {
const apiKey = process.env.CURSOR_API_KEY;
if (!apiKey) {
console.error("CURSOR_API_KEY required");
process.exit(1);
}
try {
const client = new Cursor({ apiKey });
await client.performOperation();
} catch (err: any) {
if (err?.status === 401) {
console.error("Authentication failed – check the CURSOR_API_KEY value.");
} else if (err?.status === 403) {
console.error(
"Permission denied – the supplied key may be a user key lacking team scope."
);
} else {
throw err;
}
}
}
This pattern aligns with the error handling logic in agent-manager.ts, distinguishing between authentication failures and team-scope permission denials.
Fallback Strategy for Team vs User Keys
When both key types might be available, implement a fallback chain that prefers team-level access but degrades gracefully to user-level credentials:
async function getClient() {
const teamKey = process.env.CURSOR_TEAM_API_KEY;
const userKey = process.env.CURSOR_USER_API_KEY;
const tryKey = async (key: string | undefined) => {
if (!key) return null;
try {
const client = new Cursor({ apiKey: key });
await client.auth.whoAmI();
return client;
} catch (e) {
return null;
}
};
return (await tryKey(teamKey)) ?? (await tryKey(userKey));
}
This approach attempts the team key first for broader resource access, falling back to the user key only if the team key is unavailable or invalid.
Summary
- Team keys authorize shared resources and team-scoped operations, while user keys restrict operations to individual identity and permissions.
- The SDK validates
CURSOR_API_KEYimmediately at startup inagent-manager.ts(line 228), throwing specific errors when the variable is missing. - HTTP 401 responses indicate invalid tokens, requiring credential verification.
- HTTP 403 responses indicate insufficient scope, often occurring when a user key attempts to access team-level resources.
- Implement fallback logic to gracefully degrade from team keys to user keys when permissions are insufficient.
Frequently Asked Questions
What's the difference between a 401 and 403 error in the Cursor SDK?
A 401 Unauthorized error means the API key is invalid, expired, or malformed, and the SDK cannot authenticate the request. A 403 Forbidden error means the key is valid but lacks permission for the specific operation, such as using a user key to access team-wide resources. Check err.status to distinguish between these cases and provide appropriate user guidance.
Can I use team and user keys interchangeably?
No. Team keys grant access to all resources belonging to the team and attribute actions to the team identity, while user keys only access resources visible to that specific user and attribute actions to the individual. Attempting to use a user key for team-level operations results in a 403 permission error, as implemented in the permission checking logic of agent-manager.ts.
How do I check if my API key is valid before making expensive API calls?
Call the client.auth.whoAmI() method after initializing the Cursor client. This lightweight validation endpoint returns user or team metadata without consuming significant resources. If this call throws a 401 error, the key is invalid; if it succeeds, the client is properly authenticated and ready for further operations.
Where should I store my CURSOR_API_KEY in production?
Store the key in environment variables only, never committing it to source control. The cursor/plugins repository expects the variable via process.env.CURSOR_API_KEY, matching the pattern in probe-models.ts (line 94). For applications requiring both key types, use CURSOR_TEAM_API_KEY and CURSOR_USER_API_KEY as distinct environment variables and implement fallback logic to select the appropriate credential.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →