Summary
POST /fapi/v1/marginType returns error -4067 ("Position side cannot be changed if there exists open orders.") for pairs in a state where:
- 0 open positions (
positionAmt: 0)
- 0 open orders (
fetch_open_orders returns [])
- Margin mode is already correctly set to the target value (e.g.,
CROSSED)
- Account is in One-Way Position Mode (
dualSidePosition: false)
Empirically, the same call returns the expected -4046 ("No need to change margin type.") when the pair has an open position. The -4067 response in the no-position case appears to be a misclassified internal validation — the message text refers to "open orders" that demonstrably do not exist.
Reproduction
import ccxt
ex = ccxt.binance({
"apiKey": "<your_key>",
"secret": "<your_secret>",
"options": {"defaultType": "future"},
})
# Pre-state verification (all clean):
assert ex.fetch_open_orders("ADA/USDT:USDT") == []
positions = ex.fapiPrivateV2GetPositionRisk()
ada = next(p for p in positions if p["symbol"] == "ADAUSDT")
assert float(ada["positionAmt"]) == 0
assert ada["marginType"] == "cross"
# Call that triggers the bug:
ex.set_margin_mode("cross", "ADA/USDT:USDT")
# → ccxt.OperationRejected:
# binance {"code":-4067,
# "msg":"Position side cannot be changed if there exists open orders."}
# Compare with a pair that has a position (same margin mode):
ex.set_margin_mode("cross", "XRP/USDT:USDT")
# → returns: {"code":-4046, "msg":"No need to change margin type."}
Direct REST equivalent:
POST https://fapi.binance.com/fapi/v1/marginType
symbol=ADAUSDT
marginType=CROSSED
timestamp=...
signature=...
→ HTTP 400 {"code":-4067, "msg":"Position side cannot be changed if there exists open orders."}
Expected behavior
Either:
- (a) Return
-4046 ("No need to change margin type.") consistent with the same call against a pair that has a position, OR
- (b) Return a clearer error code/message that reflects the actual cause (margin already correct), so callers can distinguish from genuine position-mode-conflict errors
Actual behavior
Returns -4067 with a misleading message about non-existent open orders.
Impact
Production trading system using setMarginType before every entry order experienced silent trade blocking for 4 days on one bot:
- 2,490 entry signals evaluated across 24 hours, 0 trades opened
- Bot retried
setMarginType 5× per attempt via standard retry/backoff → all aborted at -4067
- Trading resumed within 90 seconds of applying an adapter-side workaround that treats
-4067 as a no-op when margin is already correct
- Affected pairs: those with no open position (ADA, AVAX). Unaffected: pairs with active positions (BTC, ETH, BNB, SOL, XRP, LINK) — those returned
-4046 and the caller continued normally.
Prior art
- ccxt#11936 (Feb 2022, closed without fix) — same
-4067 issue raised in ccxt layer
- freqtrade#6431 (Feb 2022, closed without fix) — earlier attempt at adapter workaround, closed for over-broad scope
- freqtrade#13188 (filed 2026-05-25) — narrow adapter workaround for
-4067 only, pending review
This behavior has been reported across at least two major OSS trading libraries over the past 4 years without upstream resolution.
Suggested resolution (priority order)
- Return
-4046 for the idempotent case — when margin mode already matches request and pair has no positions/orders, the response should be the same as for a pair with a position (i.e. "no need to change"). This makes the endpoint truly idempotent and removes the need for client-side workarounds.
- Update error message if
-4067 is intentional in this scenario: change "if there exists open orders" to reflect the actual cause (e.g. "margin already set" or "no operation needed"). The current message has cost the ecosystem 4+ years of repeated reports across multiple libraries.
- Document the
-4067 edge case in the error code reference if the behavior is intentional and won't change.
Environment
- Endpoint:
POST /fapi/v1/marginType (Binance Futures REST API v1)
- Account: USDT-M Futures, One-Way Position Mode
- Reproduced via:
ccxt 4.5.39, direct REST via fapiPrivatePostMarginType
- Date observed: 2026-05-21 to 2026-05-25
Note: filing here because this repository is the most active official Binance Python connector channel. Issue is server-side API behavior, not a connector bug — please redirect if there is a better channel for API-behavior reports (could not find one publicly documented).
Summary
POST /fapi/v1/marginTypereturns error-4067("Position side cannot be changed if there exists open orders.") for pairs in a state where:positionAmt: 0)fetch_open_ordersreturns[])CROSSED)dualSidePosition: false)Empirically, the same call returns the expected
-4046("No need to change margin type.") when the pair has an open position. The-4067response in the no-position case appears to be a misclassified internal validation — the message text refers to "open orders" that demonstrably do not exist.Reproduction
Direct REST equivalent:
Expected behavior
Either:
-4046("No need to change margin type.") consistent with the same call against a pair that has a position, ORActual behavior
Returns
-4067with a misleading message about non-existent open orders.Impact
Production trading system using
setMarginTypebefore every entry order experienced silent trade blocking for 4 days on one bot:setMarginType5× per attempt via standard retry/backoff → all aborted at-4067-4067as a no-op when margin is already correct-4046and the caller continued normally.Prior art
-4067issue raised in ccxt layer-4067only, pending reviewThis behavior has been reported across at least two major OSS trading libraries over the past 4 years without upstream resolution.
Suggested resolution (priority order)
-4046for the idempotent case — when margin mode already matches request and pair has no positions/orders, the response should be the same as for a pair with a position (i.e. "no need to change"). This makes the endpoint truly idempotent and removes the need for client-side workarounds.-4067is intentional in this scenario: change "if there exists open orders" to reflect the actual cause (e.g. "margin already set" or "no operation needed"). The current message has cost the ecosystem 4+ years of repeated reports across multiple libraries.-4067edge case in the error code reference if the behavior is intentional and won't change.Environment
POST /fapi/v1/marginType(Binance Futures REST API v1)ccxt 4.5.39, direct REST viafapiPrivatePostMarginTypeNote: filing here because this repository is the most active official Binance Python connector channel. Issue is server-side API behavior, not a connector bug — please redirect if there is a better channel for API-behavior reports (could not find one publicly documented).