Error Codes Reference
Every error from both MCP and REST uses the same ErrorResult schema:
{
"error": "TIER_BLOCKED",
"message": "Parameters pop_model require premium (current tier: free). Allowed parameters for create_strategy: dte_max, dte_min, iv_rank_gate_mode, iv_rank_threshold, max_capital, ticker, top_k.",
"suggestion": "Upgrade to premium for access to this parameter or algorithm.",
"expected": null,
"received": {"pop_model": "cornish_fisher"},
"see_tool": null,
"tier_upgrade_hint": true
}
Agents parse error (machine code) to branch logic. message is for display.
suggestion guides the agent toward the correct next action.
Validation Errors
These are raised when input fails validation before any work is done.
| Code |
HTTP |
When raised |
Agent action |
INVALID_UUID |
422 |
A parameter expected to be a UUID is malformed |
Use the UUID from the source tool (see see_tool) |
INVALID_VALUE |
422 |
An enum parameter has a value outside the allowed set |
Use one of the values listed in suggestion |
OUT_OF_RANGE |
422 |
A numeric parameter is outside min/max bounds |
Adjust to the range shown in suggestion |
INVALID_TICKER |
422 |
Ticker symbol not found as a tradeable asset on Alpaca |
Check the symbol spelling; use standard US equity/ETF tickers |
INVALID_EMAIL |
422 |
Email address fails format validation |
Provide a valid email address |
INVALID_QTY |
422 |
qty parameter is less than 1 |
Set qty >= 1 |
INVALID_RANK |
422 |
strategy_rank is less than 1 |
Use rank from top_strategies in results (1 = best) |
INVALID_CANDIDATE |
422 |
candidate is not a dict or has wrong structure |
Pass a candidate from find_iron_condors |
MISSING_CANDIDATE_FIELDS |
422 |
Candidate dict is missing required strike/credit fields |
Pass a complete candidate from find_iron_condors |
MISSING_API_KEY |
422 |
api_key is empty or not provided |
Ask the user for their broker API key |
MISSING_API_SECRET |
422 |
api_secret is empty or not provided |
Ask the user for their broker API secret |
MISSING_TICKER |
422 |
ticker not provided and no saved_strategy_id to load it from |
Pass ticker directly or use saved_strategy_id |
Workflow Errors
These indicate the caller is using the wrong tool or wrong sequence.
| Code |
HTTP |
When raised |
Agent action |
STRATEGY_NOT_CANDIDATE |
422 |
Passed a strategy config (from create_strategy) where a tradeable candidate (from find_iron_condors) was expected |
Call find_iron_condors first, then pass a candidate to propose_entry |
NO_HISTORY |
404 |
get_optimization_history called but user has no past runs |
Call create_strategy first |
POSITION_NOT_FOUND |
404 |
Position ID not found for this user |
Call get_positions to list valid position IDs |
SAVED_STRATEGY_NOT_FOUND |
404 |
saved_strategy_id not found or not owned by this user |
Call get_saved_strategies to list valid IDs |
Access and Auth Errors
| Code |
HTTP |
When raised |
Agent action |
TIER_BLOCKED |
403 |
Parameter or algorithm value not allowed for the user's tier |
Remove the blocked parameter, or inform the user about premium |
QUOTA_EXCEEDED |
429 |
Daily per-tool quota exhausted (free tier) |
Wait for reset or upgrade to premium |
BROKER_NOT_CONNECTED |
409 |
Trading tool called without an active broker account |
Call set_broker_keys or connect_broker first |
ACCESS_DENIED |
403 |
Attempted to access a position not owned by this user |
Verify the position ID belongs to this user |
NOT_SERVICE_MANAGED |
403 |
Position was opened externally, not through Assistatron |
Only positions opened through Assistatron can be managed |
SESSION_INVALID |
401 |
Bearer token or session cookie is invalid or expired |
Call login or refresh_session |
UNAUTHORIZED |
401 |
No authentication credentials provided |
Provide a Bearer token |
Proposal Errors
| Code |
HTTP |
When raised |
Agent action |
PROPOSAL_NOT_FOUND |
404 |
Proposal ID not found or not owned by this user |
Check the proposal_id; proposals expire after 60s |
PROPOSAL_EXPIRED |
410 |
Proposal TTL (60 seconds) has elapsed |
Create a new proposal via propose_entry, propose_roll, or propose_exit |
PROPOSAL_ALREADY_CONSUMED |
409 |
Proposal was already confirmed |
No action needed; the order was already submitted |
Risk and Trading Errors
| Code |
HTTP |
When raised |
Agent action |
ENTRY_REJECTED |
422 |
Risk gate rejected the candidate |
Review reasons in the response; adjust the candidate or risk settings |
AUTOROLL_DISABLED |
501 |
Autoroll execution not enabled on this deployment |
Use suggest_iron_condor_autoroll for read-only alerts instead |
Subscription Errors
| Code |
HTTP |
When raised |
Agent action |
ALREADY_SUBSCRIBED |
409 |
User already has an active subscription |
No action needed |
SUBSCRIPTION_NOT_FOUND |
404 |
Subscription not found for this user |
Check subscription status with get_subscription |
Server Errors
| Code |
HTTP |
When raised |
Agent action |
SERVICE_NOT_CONFIGURED |
503 |
A required service is not available on this deployment |
Contact support |
SERVER_MISCONFIGURED |
500 |
Server missing required configuration (e.g. signing key) |
Contact support |
INTERNAL_ERROR |
500 |
Unhandled exception |
Retry or contact support |
Generic HTTP Fallbacks
When a route raises HTTPException without a specific handler, the code
is derived from the HTTP status:
| HTTP Status |
Default Code |
| 400 |
BAD_REQUEST |
| 401 |
UNAUTHORIZED |
| 403 |
FORBIDDEN |
| 404 |
NOT_FOUND |
| 409 |
CONFLICT |
| 422 |
VALIDATION_ERROR |
| 429 |
TOO_MANY_REQUESTS |
| 500 |
INTERNAL_ERROR |
| 503 |
SERVICE_UNAVAILABLE |
Response Schema
Both MCP and REST return errors using the same JSON shape:
{
"error": "string (required) — machine-readable code",
"message": "string (required) — human-readable description",
"suggestion": "string (optional) — what to do instead",
"expected": "string (optional) — what was expected",
"received": "object (optional) — what was actually passed",
"see_tool": "string (optional) — tool to call instead",
"tier_upgrade_hint": "boolean (optional) — true if premium resolves it"
}