---
title: "ProjectX Error Codes by Endpoint: What Each Number Means"
slug: projectx-error-codes-by-endpoint-what-each-number-means
date: 2026-09-17
modified: 2026-09-17
author: Bhavishya Goyal
excerpt: ""
meta_description: "ProjectX error codes change meaning by endpoint: errorCode 2 alone has seven. Decode login, order and position errors."
focus_keyword: projectx error codes
canonical_url: "https://blog.pickmytrade.io/projectx-error-codes-by-endpoint-what-each-number-means/"
og_title: "ProjectX Error Codes by Endpoint: What Each Number Means"
og_description: "ProjectX error codes change meaning by endpoint: errorCode 2 alone has seven. Decode login, order and position errors."
og_image: "https://blog.pickmytrade.io/wp-content/uploads/2026/09/Gemini_Generated_Image_cmh3m2cmh3m2cmh3-1024x572.avif"
schema_type: FAQPage
categories:
  - Prop Firm
  - Trading
tags: []
reading_time: 21
word_count: 4962
robots: "index, follow"
lang: en-US
---

# ProjectX Error Codes by Endpoint: What Each Number Means

On September 17, 2026, we sent the ProjectX API a login with a username that doesn’t exist. It answered HTTP 200 with this body:

Table of Contents

1. [How are ProjectX error codes numbered by endpoint?](https://blog.pickmytrade.io/#how-are-projectx-error-codes-numbered-by-endpoint)
2. [Build the error map from the spec](https://blog.pickmytrade.io/#build-the-error-map-from-the-spec)
3. [What to log with every failure](https://blog.pickmytrade.io/#what-to-log-with-every-failure)
4. [Which layer rejected the request?](https://blog.pickmytrade.io/#which-layer-rejected-the-request)
5. [What our test calls returned](https://blog.pickmytrade.io/#what-our-test-calls-returned)
6. [When ProjectX rejects an order it already accepted](https://blog.pickmytrade.io/#when-projectx-rejects-an-order-it-already-accepted)
7. [What do ProjectX login error codes 3, 7, 9 and 10 mean?](https://blog.pickmytrade.io/#what-do-projectx-login-error-codes-3-7-9-and-10-mean)
8. [What’s inside errorCode 2 when Order/place rejects an order?](https://blog.pickmytrade.io/#whats-inside-errorcode-2-when-order-place-rejects-an-order)
9. [The Position Brackets rejection](https://blog.pickmytrade.io/#the-position-brackets-rejection)
10. [Trailing stop, customTag and order type rejections](https://blog.pickmytrade.io/#trailing-stop-customtag-and-order-type-rejections)
11. [Cancel and modify number the same failure differently](https://blog.pickmytrade.io/#cancel-and-modify-number-the-same-failure-differently)
12. [Close and partial close are off by one](https://blog.pickmytrade.io/#close-and-partial-close-are-off-by-one)
13. [The rejections your own TopstepX Risk Settings cause](https://blog.pickmytrade.io/#the-rejections-your-own-topstepx-risk-settings-cause)
14. [Traps that only show up under automation](https://blog.pickmytrade.io/#traps-that-only-show-up-under-automation)
15. [Why are orders rejected at 3:10 PM CT or during a pause?](https://blog.pickmytrade.io/#why-are-orders-rejected-at-310-pm-ct-or-during-a-pause)
16. [What causes ContractNotFound and ContractNotActive around a roll?](https://blog.pickmytrade.io/#what-causes-contractnotfound-and-contractnotactive-around-a-roll)
17. [History and bar errors](https://blog.pickmytrade.io/#history-and-bar-errors)
18. [Is it safe to resend an order after a ProjectX error?](https://blog.pickmytrade.io/#is-it-safe-to-resend-an-order-after-a-projectx-error)
19. [How do ProjectX errors show up in PickMyTrade?](https://blog.pickmytrade.io/#how-do-projectx-errors-show-up-in-pickmytrade)
20. [Frequently asked questions](https://blog.pickmytrade.io/#frequently-asked-questions)
21. [Before you blame the bot](https://blog.pickmytrade.io/#before-you-blame-the-bot)

```
{"token":null,"success":false,"errorCode":3,"errorMessage":null}
```

On the login endpoint, `errorCode: 3` means InvalidCredentials. The same 3 from `Order/place` means InsufficientFunds, and from `Auth/validate` it means ExpiredToken. So which one just hit your bot?

That’s the trap with ProjectX error codes. They aren’t one list. The Gateway API behind TopstepX defines 17 separate error enums, and the number 2 alone carries seven meanings. Our [Order/place code table](https://blog.pickmytrade.io/projectx-error-codes-fix/) covers the ten order-placement codes a TradingView bridge runs into. This guide decodes everything around them. You’ll see which endpoint sent a number, the message text behind it, the rejections your own TopstepX settings cause, and when resending an order is safe.

&gt; **Key Takeaways**
&gt; 
&gt; 
&gt; 
&gt; 
&gt; 
&gt; 
&gt; 
&gt; - ProjectX defines 17 error enums, so `errorCode: 2` has seven meanings. Read every code together with its endpoint.
&gt; - On `Order/place`, code 2 covers all validation rejections, and `errorMessage` names the cause.
&gt; - Pending codes mean the outcome is unknown. Check orders and positions before resending.
&gt; - Token and routing failures return HTTP errors with no errorCode at all.

**errorCode** is the integer ProjectX puts in a response’s JSON body, next to a `success` flag, and 0 always means success. **errorMessage** is the optional text field beside it, and it’s often `null`. **An enum** is a fixed list of named numbers. ProjectX gives each endpoint’s response its own list, which is where the confusion starts.

## How are ProjectX error codes numbered by endpoint? {#how-are-projectx-error-codes-numbered-by-endpoint}

ProjectX numbers its error codes per endpoint, not globally. We analyzed the live Gateway API spec on September 17, 2026, and found 17 error enums holding 65 failure codes under 30 distinct names. Only `0 = Success` means the same thing everywhere. Every other number depends on which endpoint sent it.

In fact, three of those enums, for account search, contract search and available contracts, define nothing but Success. The other 14 reuse the same small integers for unrelated failures. Take errorCode 1. It’s UserNotFound on login and InvalidSession on logout and validate. On contract lookups and historical bars it’s ContractNotFound, on order lookup by ID it’s OrderNotFound, and on every order, position and trade call it’s AccountNotFound.

How many meanings each ProjectX errorCode carries
  The same errorCode number means different things depending on which endpoint returned it.
  
  One number, up to seven meanings
  Distinct error names per errorCode across 17 ProjectX enums
  errorCode 1
  
  5
  errorCode 2
  
  7 meanings
  errorCode 3
  
  6
  errorCode 4
  
  6
  errorCode 5
  
  5
  errorCode 6
  
  4
  errorCode 7
  
  4
  errorCode 8
  
  3
  errorCode 9
  
  3
  errorCode 10
  
  2
  
  errorCode 2 can mean any of these:
  PasswordVerificationFailed · UnknownError
  SessionNotFound · UnitInvalid · OrderRejected
  OrderNotFound · PositionNotFound
  Code 0 is always Success. Our count, API spec as of Sept 17, 2026.

Why does that matter once a bot is live? A bot that maps codes with one global table will mislabel most failures. It’ll report a failed position close as a funding problem, or a bars request with a wrong contract ID as a missing account. Then someone spends an hour fixing the wrong thing.

### Build the error map from the spec {#build-the-error-map-from-the-spec}

The fix is dull, and it works. Key your error map by endpoint first and code second, and you don’t even have to type it. Every enum is machine-readable in the Swagger spec at `https://api.topstepx.com/swagger/v1/swagger.json`, with the names in `x-enumNames`, and this standard-library Python builds the whole map from it:

```
import json
from urllib.request import urlopen

SPEC_URL = "https://api.topstepx.com/swagger/v1/swagger.json"
spec = json.load(urlopen(SPEC_URL, timeout=10))
defs = spec["definitions"]

ERRORS = {}  # {"/api/Order/place": {0: "Success", 1: "AccountNotFound", ...}, ...}
for path, ops in spec["paths"].items():
    for op in ops.values():
        ref = op["responses"]["200"]["schema"].get("$ref")
        if not ref:
            continue  # /api/Status/ping returns plain text
        code_ref = defs[ref.split("/")[-1]]["properties"]["errorCode"]["$ref"]
        enum = defs[code_ref.split("/")[-1]]
        ERRORS[path] = dict(zip(enum["enum"], enum["x-enumNames"]))

def describe(path, body):
    name = ERRORS.get(path, {}).get(body["errorCode"], "unknown")
    return f"{path} errorCode {body['errorCode']} = {name}: {body.get('errorMessage')}"

print(describe("/api/Position/closeContract", {"errorCode": 6, "errorMessage": None}))
```

We ran it on September 17, 2026. It mapped 20 endpoints, since ping returns plain text, and printed OrderPending for that code 6 from `Position/closeContract`. Swap in `Position/partialCloseContract` and the same 6 comes back as OrderRejected.

### What to log with every failure {#what-to-log-with-every-failure}

Log the raw message next to the number, too, because the message often carries the real reason. A log line you can actually debug from, whether the bot runs on your desk or behind a bridge, needs four things:

- The endpoint path, such as `/api/Order/place`
- The HTTP status code
- `errorCode` and `errorMessage` exactly as returned, including a `null` message, because null tells you something too
- The `X-Correlation-Id` response header

Every response we received carried that correlation header, successes and failures alike, with a new value each time. It pins one request in your logs when you need to match a failure to a moment.

## Which layer rejected the request? {#which-layer-rejected-the-request}

A ProjectX request can fail at three layers. Only one of them uses errorCode. HTTP errors such as 401 and 400 arrive first, before any errorCode exists. Next comes the `errorCode` in the body. Then, even after `success: true`, the order itself can end up with status 5, Rejected.

### What our test calls returned {#what-our-test-calls-returned}

We tested the first layer against the live API on September 17, 2026, with no account attached. Here’s what came back:

| What we sent | HTTP status | What came back | What it tells you |
| --- | --- | --- | --- |
| Login with a made-up username and key | 200 | success: false, errorCode: 3, errorMessage: null | Bad credentials, despite the 200 |
| Login with an empty JSON body | 400 | A validation error naming userName and apiKey | The login check never ran |
| Account search with no token | 401 | Empty body, header WWW-Authenticate: Bearer | No token reached the API |
| Account search with a malformed token | 401 | Empty body, header WWW-Authenticate: Bearer error="invalid_token" | A token arrived and was refused |
| GET instead of POST on Order/place | 405 | Empty body, header Allow: POST | Wrong HTTP method |
| A misspelled path, /api/Orders/place | 404 | Empty body | Wrong URL |

That table holds two details that save real debugging time. First, a failed login is still HTTP 200, so a client that only checks status codes treats bad credentials as success. Second, the `WWW-Authenticate` header tells you which 401 you’ve got. Plain `Bearer` means no token arrived at all, which usually points to a header-building bug. `Bearer error="invalid_token"` means a token arrived and was refused, typically because it expired or was malformed.

Bad request bodies belong to this layer too, and the documentation can hand you one. We found that the Search for Account page’s cURL sample uses `-X 'undefined'` and sends no body, although the endpoint requires `onlyActiveAccounts`. Copy the body from the Placing Your First Order page instead: `{"onlyActiveAccounts": true}`.

And rate limits? They live in this layer as well. ProjectX allows 200 requests per 60 seconds on most endpoints and 50 per 30 seconds on historical bars, then answers with HTTP 429. Our guide to [API rate limits in trading automation](https://blog.pickmytrade.io/api-rate-limits-trading-automation/) covers backoff.

### When ProjectX rejects an order it already accepted {#when-projectx-rejects-an-order-it-already-accepted}

The third layer is the one that catches people out. `errorCode: 0` on `Order/place` only means ProjectX accepted the order. A fill comes later, if at all. **OrderStatus** is the lifecycle field every order carries after that: 1 Open, 2 Filled, 3 Cancelled, 4 Expired, 5 Rejected, 6 Pending, 7 PendingCancellation and 8 Suspended.

ProjectX can reject a working order well after accepting it. For example, TopstepX rejects a buy stop the moment it triggers if the fill would push the account past its maximum position size, then pulls it from working orders. No errorCode reports that. You’ll only see it through `Order/search` or the order events on the user hub.

`Order/searchOpen` also leaves out suspended bracket child orders. To see them, query `/api/Order/v2/query` with `statuses` set to Open and Suspended.

## What do ProjectX login error codes 3, 7, 9 and 10 mean? {#what-do-projectx-login-error-codes-3-7-9-and-10-mean}

A failed login returns HTTP 200 with `success: false`, and four codes cover what you’ll actually meet. Our made-up username returned 3. That’s not the UserNotFound (1) the login enum also defines. A wrong username, a mistyped key and a revoked key all look the same. Can you tell them apart? Not from the response alone.

| errorCode | Name | What it means | How to clear it |
| --- | --- | --- | --- |
| 3 | InvalidCredentials | The username and API key don’t match an active key | Send your TopstepX sign-in username. An email address fails. Generate a fresh key and copy all of it. |
| 7 | AgreementsNotSigned | Platform agreements are waiting. The message reads “Please log into the ProjectX platform and complete the required agreements”. | Sign in to TopstepX, accept them, then retry |
| 9 | ApiSubscriptionNotFound | No active API subscription, so ProjectX can’t issue a token | Check the ProjectX Dashboard subscription and its link to your TopstepX profile |
| 10 | ApiKeyAuthenticationDisabled | API key login is switched off for your firm | Contact Topstep |

Two failures never reach those codes. A body missing `userName` or `apiKey` gets HTTP 400 before any login check runs, exactly as our empty-body test showed. And a session token lasts 24 hours. After that, every call fails with HTTP 401 until you get a new one.

![Close-up of Python code on a screen with a try and except block, the pattern a bot uses to catch a failed login or API call.](https://images.pexels.com/photos/5223887/pexels-photo-5223887.jpeg?auto=compress&amp;cs=tinysrgb&amp;w=1200&amp;fm=webp)

It’s easy to miss that logging in again issues a new token without invalidating tokens already in use. As a result, two scripts sharing one API key won’t knock each other offline.

Expiry is murkier, because the documentation disagrees with itself. The authentication page says to refresh with `Auth/validate` before the token expires, while the validate page says to re-validate once it has, and validate’s own enum even includes ExpiredToken (3).

We couldn’t test an expired token without an account. A validate call with no token simply returned 401. The safe pattern is to refresh well inside the 24 hours, and to log in again on any 401.

## What’s inside errorCode 2 when Order/place rejects an order? {#whats-inside-errorcode-2-when-order-place-rejects-an-order}

On `Order/place`, errorCode 2 (OrderRejected) is a catch-all that covers every validation rejection, including unsupported order types, invalid side or size, bad trailing-stop prices, duplicate `customTag` values and bracket rules. The number alone tells you almost nothing, so the reason you actually need sits in `errorMessage`, and that’s the field to log.

### The Position Brackets rejection {#the-position-brackets-rejection}

The bracket message is the one to know first:

`Brackets cannot be used with Position Brackets. You must enable Auto OCO Brackets.`

It fires when an order includes `stopLossBracket` or `takeProfitBracket` while the account still uses Position Brackets, TopstepX’s default.

&gt; **Heads up:** ProjectX still creates the rejected order, however, and returns its ID in `orderId`. In other words, a returned `orderId` isn’t proof of a live order.

To clear it, go completely flat first, because TopstepX won’t switch bracket types while you hold a position. Then open Settings, choose Risk Settings, click Switch to Auto OCO Brackets and save the bracket setup. Or skip the bracket objects and manage exits another way.

### Trailing stop, customTag and order type rejections {#trailing-stop-customtag-and-order-type-rejections}

Trailing stops add five more:

| errorMessage | What triggered it | How to clear it |
| --- | --- | --- |
| Trail Distance not set. | Order type 5 sent without trailPrice | Send trailPrice as a price level |
| Invalid trail price. Price is not aligned to tick size. | trailPrice isn’t a multiple of the tick size | Round the price to a valid tick |
| Cannot trail without a last price | The contract hasn’t traded yet | Wait for a first trade, then resend |
| Invalid symbol | No quote is available for the contract | Check the contract ID and the session |
| Trail Distance exceeds maximum (1000) | The distance works out above 1,000 ticks | Send the price level where the trail should start |

Why would a trail ever work out above 1,000 ticks? Almost always because someone sent a distance where `trailPrice` expects an absolute price. Take a contract with a 0.01 tick that last traded at 70.50. A 6-tick sell trail needs `trailPrice` 70.44. Send 0.06 instead and ProjectX reads a price near zero, works out a 7,044-tick distance and rejects it.

Code 2 hides two more causes in plain sight. **A customTag** is an optional label you attach to an order, and it must be unique across the account, so ProjectX rejects any order that reuses one. The enum also lists StopLimit as order type 3, but the Place Order page documents only types 1, 2, 4, 5, 6 and 7. We’d treat 3 as unsupported.

What about the other nine `Order/place` codes, from AccountNotFound (1) to AccountRejected (10)? Our [plain-English guide to the ten Order/place codes](https://blog.pickmytrade.io/projectx-error-codes-fix/) covers them, so we won’t repeat them here.

## Cancel and modify number the same failure differently {#cancel-and-modify-number-the-same-failure-differently}

`Order/cancel` and `Order/modify` share one numbering. It doesn’t match `Order/place`. On both, 2 means OrderNotFound, 3 Rejected, 4 Pending, 5 UnknownError and 6 AccountRejected. Modify adds 7 for ContractNotFound. A code 3 from modify has nothing to do with funds, whatever 3 means on place.

The clearest example is a misaligned trailing-stop price. Send it through `Order/place` and you get errorCode 2. Send the same bad `trailPrice` through `Order/modify` and you get errorCode 3, with identical message text. Same mistake, different number.

Cancel and modify aren’t the only pair that renumbers a failure. A contract ID ProjectX doesn’t recognize, for instance, arrives under four different numbers depending on the call:

The same ProjectX failure under different errorCode numbers
  Map codes per endpoint, never with one global list.
  
  Same failure, different number
  The errorCode each endpoint uses for the same problem
  ContractNotFound
  
  8
  place
  
  7
  modify
  
  3
  close · partial
  
  1
  lookup · bars
  AccountRejected
  
  10
  place
  
  6
  cancel · modify
  
  8
  close
  
  9
  partial close
  Still processing (OrderPending, Pending)
  
  6
  place
  
  4
  cancel · modify
  
  6
  close
  
  7
  partial close
  Engine rejection (OrderRejected, Rejected)
  
  2
  place
  
  3
  cancel · modify
  
  5
  close
  
  6
  partial close
  UnknownError
  
  7
  place
  
  5
  cancel · modify
  
  7
  close
  
  8
  partial close
  
  8
  login
  
  4
  validate
  
  2
  logout
  place = Order/place · close = Position/closeContract
  partial close = Position/partialCloseContract
  lookup = Contract/searchById · bars = History/retrieveBars

&gt; **Watch out:** modify has no maximum-distance check. `Order/modify` accepts the 0.06 mistake that `Order/place` rejects, and your stop starts trailing 7,044 ticks behind the market with no error and no warning. If your code modifies trailing stops, check the price level yourself before you send it.

Cancel’s code 6, AccountRejected, comes with two messages. “Follower accounts cannot cancel orders” means the account mirrors a leader in a copy group, so cancel through the leader. “Live accounts not supported” means the account is live or brokerage, because this endpoint only works on simulated accounts. That matches Topstep’s rulebook anyway, since its Live Funded Account rules prohibit [automated trading](https://pickmytrade.io "PickMyTrade - Automated Trading") through the ProjectX API.

Before you debug either one, pull the account from `Account/search`. Its `simulated` and `canTrade` fields tell you in one call whether the account is simulated and allowed to trade.

## Close and partial close are off by one {#close-and-partial-close-are-off-by-one}

`Position/closeContract` and `Position/partialCloseContract` do nearly the same job, and their error codes match from 1 to 4. But partial close inserts InvalidCloseSize at 5 and pushes every later code down one slot, so a 6 from close means OrderPending while a 6 from partial close means OrderRejected.

| errorCode | Position/closeContract | Position/partialCloseContract |
| --- | --- | --- |
| 1 | AccountNotFound | AccountNotFound |
| 2 | PositionNotFound | PositionNotFound |
| 3 | ContractNotFound | ContractNotFound |
| 4 | ContractNotActive | ContractNotActive |
| 5 | OrderRejected | InvalidCloseSize |
| 6 | OrderPending | OrderRejected |
| 7 | UnknownError | OrderPending |
| 8 | AccountRejected | UnknownError |
| 9 | not used | AccountRejected |

Which of these need special handling in code? Two of them do. InvalidCloseSize (5) means the size was zero, negative or larger than the open position, which often means the bot’s position count went stale, so re-read `Position/searchOpen` before closing.

OrderRejected (6) has two causes and no message to tell them apart: either the market was closed or halted, or the engine had no current price to close against. `errorMessage` is `null` either way. Both clear once the market reopens and starts quoting again.

PositionNotFound (2) deserves a second look before you log it as a failure. On a flatten command it often just means you’re already flat. Right after a contract roll, though, it can also mean the bot is closing the new month while the position still sits in the old one.

## The rejections your own TopstepX Risk Settings cause {#the-rejections-your-own-topstepx-risk-settings-cause}

TopstepX’s Risk Settings page holds 10 tools, and several of them block orders just as a broker rejection would. Your bot only sees a refused order. Nothing in the API response says the block came from your own settings, so rule these out before you touch the code.

These settings can refuse an order outright:

| Setting | What it does to new orders | Can you reverse it? |
| --- | --- | --- |
| Symbol Block | Rejects every order for that symbol. PickMyTrade shows the rejection as “symbol is blocked”. | Yes, remove the block |
| Contract Limits | Rejects any order that would push open positions plus working orders past a per-symbol cap on that side. Brackets don’t count. | Yes, change the limit |
| Trade Limits | Blocks new positions and orders once you hit a daily or weekly trade count. With a position open, only stops, targets, breakeven, flatten and cancel-all actions go through. | No |
| Trade Clock | Blocks new orders for a set time while you manage the open trade | No, it runs until the timer ends or the trade closes |
| Personal Daily Loss Limit or Profit Target, set to Liquidate and Block | Flattens the account and blocks trading until the next trading day | No |
| Manual Lock-Out | Liquidates positions, cancels working orders and blocks trading for the time you pick | No |

### Traps that only show up under automation {#traps-that-only-show-up-under-automation}

Trade Limits don’t cancel orders placed before the limit hit, so a resting entry stays working after the lockout unless you cancel it. Contract Limits, on the other hand, count working orders on each side, so a bot that stacks resting entries can get its next order rejected before any of them fill.

Copy trading adds one more. In a TopstepX trade copier, you can’t create, close or adjust orders directly on a Follower account, and the API’s cancel endpoint enforces the same rule, as the cancel section above showed. TopstepX also ignores Trade Limits on Follower accounts.

Where should you look first? Open Settings, then Risk Settings, on the exact account the bot trades. The bracket error and the symbol block, both on PickMyTrade’s alert errors list, are fixed on that one screen.

## Why are orders rejected at 3:10 PM CT or during a pause? {#why-are-orders-rejected-at-310-pm-ct-or-during-a-pause}

Topstep requires every position closed by 3:10 PM CT on weekdays and reopens trading at 5:00 PM CT, so orders sent inside that window fail. On `Order/place`, a closed market returns OutsideTradingHours (5), while PickMyTrade’s help pages show this alert-log message instead: “Trading is currently unavailable. The instrument is not in an active trading status.”

When TopstepX rejects new orders during the day
  Time-based rejections clear on their own. Resending during the window only repeats the error.
  
  When TopstepX turns new orders away
  One trading day in Central Time, 5:00 PM to 5:00 PM
  Equity index, FX, energy, metals, rates
  
  
  
  
  3:10 PM: orders and positions
  start auto-cancelling (3:08: flattening)
  
  CBOT grains (corn, wheat, soybeans, meal, oil)
  
  
  
  
  
  
  
  7:45-8:30 AM pause: no orders accepted
  
  
  5 PM
  
  9 PM
  
  1 AM
  
  5 AM
  
  9 AM
  
  1 PM
  
  5 PM
  
  Open
  
  Paused
  
  Closed to new orders
  
  
  
  4-5 PM roll
  Friday 3:10 PM to Sunday 5:00 PM: closed all weekend.
  CME Velocity Logic can briefly pause any product.
  Hover a product in TopstepX: orange means paused, red means closed.

The daily close isn’t the only wall. In particular, CBOT grain contracts, meaning corn, wheat, soybeans, soybean meal and soybean oil, pause every weekday from 7:45 to 8:30 AM CT, and Topstep accepts no orders on them then. CME Velocity Logic events can also pause any product briefly during a fast move.

Timing inside the window matters too. Topstep’s risk managers start flattening at 3:08 PM CT, and open positions and pending orders begin cancelling automatically at 3:10. An entry your strategy fires at 3:09 might fill. It won’t last.

How do you confirm a time-based rejection? Hover over the product in TopstepX’s product drop-down, which uses CME’s color codes: blue for pre-open, orange for paused, green for open and red for closed. A paused or closed market isn’t something to retry around, since resending inside the window just repeats the error.

## What causes ContractNotFound and ContractNotActive around a roll? {#what-causes-contractnotfound-and-contractnotactive-around-a-roll}

On `Order/place`, ContractNotFound (8) means ProjectX doesn’t recognize the contract ID at all. ContractNotActive (9) means the ID is real, but it isn’t the contract ProjectX currently marks active. The second one is the roll-week error. TopstepX moves each product to the new front month in its 4 to 5 PM CT window, on a day its own algorithm picks.

ProjectX contract IDs look like `CON.F.US.ENQ.Z26`: futures, US, a product root, then the month code and two-digit year. The roots aren’t always the ticker you’d expect. E-mini S&amp;P 500 is `EP` and E-mini Nasdaq-100 is `ENQ`, while Micro E-mini Nasdaq-100 keeps `MNQ`. Hard-code the September ID, and the bot starts getting code 9 the day December becomes the active contract.

![A pink push pin marking a date on a paper calendar, like the roll date when a futures contract stops being the active month.](https://images.pexels.com/photos/12052483/pexels-photo-12052483.jpeg?auto=compress&amp;cs=tinysrgb&amp;w=1200&amp;fm=webp)

So how does a bot always send the right month? It asks once per session. Search the product with `Contract/search`, set `live` to false when you’re using sim data, and take the result that has `activeContract: true`. Don’t store contract IDs between sessions.

Trading NQ1! through PickMyTrade? Then a second calendar is in play. Under PickMyTrade’s [contract rollover rule](https://docs.pickmytrade.io/docs/contract-rollover-rule/), continuous symbols switch to the next contract four days before expiration, with a warning six days out.

The September 2026 equity contracts expire on Friday, September 18, so that switch fell on Monday, September 14. TopstepX picks its own roll date, and nothing guarantees the two line up. The same rollover page recommends naming the exact contract month in your alerts, and roll week is when that advice pays off.

### History and bar errors {#history-and-bar-errors}

`History/retrieveBars` has a short enum of its own: 1 ContractNotFound, 2 UnitInvalid, 3 UnitNumberInvalid and 4 LimitInvalid. The Retrieve Bars page lists units 1 to 6, from Second to Month, although the spec also defines 7 for Tick. One request returns at most 20,000 bars, so page through long histories.

We found one more documentation slip here. The same page labels `contractId` as an integer, yet the spec and every example use string IDs such as `CON.F.US.RTY.Z24`. Send the string.

## Is it safe to resend an order after a ProjectX error? {#is-it-safe-to-resend-an-order-after-a-projectx-error}

Sometimes. Among ProjectX error codes, the pending codes are the risky ones. OrderPending, which is 6 on `Order/place`, means ProjectX hadn’t finished processing the order when it replied, so the order may already exist. Resend it blindly and you can double your position. The flowchart below sorts failures into four actions.

**OrderPending** means unfinished processing. It isn’t a resting limit order, which returns success with order status 1, Open. The unknown codes deserve the same caution. UnknownError is 7 on place and on close, and it doesn’t say whether anything went through.

Should you resend a failed ProjectX call?
  Pending and unknown codes are the dangerous ones: the order may already exist.
  
  
    
      
    
  
  Should you resend it?
  Triage a failed ProjectX call in this order
  
  1. HTTP status not 200?
  401, 429, 400, 404 or 405
  
  Yes
  
  401: log in again,
  then retry once.
  429: wait, then retry.
  400, 404, 405: fix
  the request itself.
  
  No
  
  2. A pending or unknown
  errorCode?
  place 6-7 · cancel/modify 4-5
  close 6-7 · partial close 7-8
  
  Yes
  
  Don't resend yet.
  Check open orders
  and positions first.
  Resend only with the
  original customTag.
  
  No
  
  3. Closed or paused market?
  OutsideTradingHours (5),
  3:10 PM CT close, pauses
  
  Yes
  
  Wait for the session
  or pause to end,
  then resend.
  
  No
  
  4. Everything else: fix the cause first
  Validation (errorCode 2 on place), funds, account,
  contract and login errors. Resending the same
  request gets the same rejection.
  ProjectX rejects a duplicate customTag with errorCode 2,
  so reusing the original tag can't open a second position.

That’s where customTag earns its keep. Give each new trading decision its own tag, and reuse that tag when you resend the same decision. ProjectX rejects a duplicate tag with errorCode 2, so a resend can’t open a second position.

If the resend does come back as a duplicate, the first order exists. Go find it instead of sending a third. Once you’ve fixed a validation error, though, send the corrected order under a fresh tag.

That said, closes are more forgiving than entries. Closing a position that’s already flat returns PositionNotFound rather than opening anything new. That makes a flatten the one command where a quick retry is low-risk.

## How do ProjectX errors show up in PickMyTrade? {#how-do-projectx-errors-show-up-in-pickmytrade}

PickMyTrade connects to TopstepX with your ProjectX username and API key, then places orders through the same Gateway API covered above. ProjectX error codes and rejection messages land in your alert log next to PickMyTrade’s own. The table quotes 6 of them from PickMyTrade’s help pages, 3 from each side, so you know where each fix lives.

| Message | Where it comes from | How to clear it |
| --- | --- | --- |
| Brackets cannot be used with Position Brackets. You must enable Auto OCO Brackets. | ProjectX, Order/place errorCode 2 | Go flat, then switch to Auto OCO Brackets in TopstepX Risk Settings |
| Trading is currently unavailable. The instrument is not in an active trading status. | ProjectX | Wait for the session, then check pauses, Symbol Block and the contract month |
| symbol is blocked | ProjectX Risk Settings | Remove the symbol from Symbol Blocks |
| Symbol Mapping Not Found | PickMyTrade | Map the symbol in your PickMyTrade settings |
| Account id … not found in user connection | PickMyTrade | Generate a new alert and select the connected account |
| Can not place order … check manual trade pause time | PickMyTrade Trading Time Settings | Check your trading window, set in Eastern Time, and any manual pause |

Step-by-step fixes for the first two messages live in PickMyTrade’s help center: the [Auto OCO Brackets switch](https://docs.pickmytrade.io/docs/oco-brackets-in-topstepx/) and the [“Trading is currently unavailable” guide](https://docs.pickmytrade.io/docs/projectx-instrument-not-in-active-trading-status/).

A bridge does take some of this off your plate. PickMyTrade manages the session token for a ProjectX connection, so you’re not writing 24-hour refresh logic or 401 handling yourself. Its continuous-symbol mapping follows the rollover rule described above.

One thing no tool changes is Topstep’s policy. Topstep treats third-party integrations as used at your own risk and requires trading activity to originate from your personal device. Read [how automation fits Topstep’s rules](https://blog.pickmytrade.io/topstepx-automation-2026/) and confirm your setup with Topstep before you automate an evaluation.

Want these rejections in an alert log instead of raw JSON? PickMyTrade’s [7-day free trial](https://pickmytrade.io/pricing/) needs no credit card, and the monthly plan is $50 with unlimited accounts.

The [Topstep connection page](https://pickmytrade.io/propfirms/topstep/) shows what’s supported. You can also read [who’s behind PickMyTrade](https://pickmytrade.io/about/) or [contact the PickMyTrade team](https://pickmytrade.io/page/contact/) with a specific error first.

## Frequently asked questions {#frequently-asked-questions}

**What does errorCode 3 mean in the ProjectX API?** 

It depends on the endpoint, because ProjectX error codes change meaning from one endpoint to the next. On login it’s InvalidCredentials, on `Order/place` it’s InsufficientFunds and on `Auth/validate` it’s ExpiredToken. Cancel and modify use 3 for Rejected, both position close calls use it for ContractNotFound, and bars use it for UnitNumberInvalid. Always read a code together with its endpoint.

 
 
**Why does a failed ProjectX login return HTTP 200?** 

ProjectX reports login failures in the response body and leaves the status code at 200. A wrong username or API key returns HTTP 200 with `success: false` and errorCode 3, which we confirmed with a test call on September 17, 2026. Only a request missing `userName` or `apiKey` gets HTTP 400. Check `success` before reading the token.

 
 
**Does success: true mean my ProjectX order filled?** 

No. On `Order/place`, `success: true` with errorCode 0 only means ProjectX accepted the order. What happens next shows up in the order’s status, where 2 means Filled and 5 means Rejected. TopstepX can still reject a working stop later, for example when it would exceed the account’s maximum position size as it triggers.

 
 
**How do I fix “Brackets cannot be used with Position Brackets”?** 

Your TopstepX account is on Position Brackets while the order includes bracket objects, so `Order/place` returns errorCode 2. Go flat, open Settings, then Risk Settings, and switch to Auto OCO Brackets. Or remove the brackets from the order. ProjectX still assigns the rejected order an ID, so don’t mistake that `orderId` for a live order.

 
 
**Can I cancel orders on a live account through the ProjectX API?** 

Not through `Order/cancel`. That endpoint only works on simulated accounts, so a live or brokerage account gets errorCode 6, AccountRejected, with the message “Live accounts not supported”. Follower accounts in a copy group get the same code with “Follower accounts cannot cancel orders”, so cancel those through the leader account.

 
 

## Before you blame the bot {#before-you-blame-the-bot}

Most ProjectX error codes stop being mysterious once you read three things together: the HTTP status, the endpoint and the `errorMessage`. With 17 enums behind the API, the number alone tells you the least. Read it last, after the endpoint and the message.

Key your error map by endpoint, and treat pending and unknown codes as a signal to check orders and positions before you resend anything. Before you rewrite a line of code, check TopstepX’s Risk Settings, the clock and the contract month. Each one can reject a perfectly good order.

Setting up API access from scratch? Start with our [Topstep API key walkthrough](https://blog.pickmytrade.io/topstep-api-getting-a-key-and-placing-your-first-automated-trade/). Once orders are flowing, the [TopstepX rules for payouts, drawdown and automation](https://blog.pickmytrade.io/topstepx-rules-payouts-drawdown-automation/) cover what a bot has to respect.

---

_Written by the PickMyTrade Team, the automated futures and execution specialists behind PickMyTrade’s TradingView-to-broker webhook routing. Editorial note: we checked error codes, messages and test responses against the live ProjectX Gateway API and its documentation on September 17, 2026, and ProjectX can change them without notice. This article is for information only, not trading or financial advice._

---

Also Checkout: [Tradeify Webhooks: Automate Trades from TradingView](https://blog.pickmytrade.io/tradeify-webhooks-tradingview-automation/)

**Automate Your TradingView Strategies**  
Connect your alerts with [PickMyTrade](https://pickmytrade.io "PickMyTrade") — automated trade execution, no coding required. [Start free →](https://pickmytrade.io/pricing)

For AI tools &amp; developers:[View Markdown →](https://blog.pickmytrade.io/projectx-error-codes-by-endpoint-what-each-number-means.md)<!--pmtA1-clean-->