Skip to content

[Futures API] setMarginType returns -4067 when margin already correct + no position + no orders #538

@spokodev

Description

@spokodev

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)

  1. 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.
  2. 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.
  3. 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).

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions