placeOrder returns HTTP 400 with a completely empty body (no JSON) — reproducible with a real, valid instrument


  • Summary

    placeOrder returns HTTP 400 with a completely empty body (Content-Length: 0) — no status/message/errorcode JSON at all, so there's no error detail to act on. Reproducible with a real, valid instrument, not just a malformed request.

    Environment

    • smartapi-python v1.5.5 (latest on PyPI)
    • App registered with a static primary IP (via SmartAPI dashboard → Add App)
    • Login (generateSession via client code + PIN + TOTP) succeeds every time

    What works fine with the same session

    • getProfile
    • rmsLimit
    • orderBook
    • position
    • getMarketData

    Headers sent (verified against the official Orders doc example, exact match)

    Authorization: Bearer <token>
    Content-Type: application/json
    Accept: application/json
    X-UserType: USER
    X-SourceID: WEB
    X-ClientLocalIP: <local ip>
    X-ClientPublicIP: <public ip>
    X-MACAddress: <mac>
    X-PrivateKey: <api key>

    Steps to reproduce

    1. placeOrder with a deliberately invalid tradingsymbol / symboltoken — expected a normal validation-error JSON response, got empty 400 instead.
    2. placeOrder with a real, valid NSE equity symbol/token (e.g. TCS-EQ) and a realistic limit price — same empty 400.
    3. Tried both via the SDK's placeOrder() / placeOrderFullResponse() and via a raw requests.post() directly to https://apiconnect.angelone.in/rest/secure/angelbroking/order/v1/placeOrder with the same headers/payload — identical result either way.

    What I've ruled out

    • Not a header problem — verified the exact header set against the official Orders documentation page, line by line.
    • Not insufficient funds/margin — the docs' own example of a real rejection (insufficient funds) returns a full JSON body with a human-readable text field. An empty body doesn't match that pattern.
    • Possibly not market hours — tests were run outside market hours (~10:30 PM IST). Per the docs, off-hours orders should become AMO orders with a normal success response (order_id returned), not fail outright — so I'm not fully confident this explains it either, though I haven't yet confirmed the same request during live market hours.

    Question

    Has anyone else hit a zero-byte 400 specifically (not a JSON error) on placeOrder? Trying to determine whether this is account-specific (some provisioning step I'm missing) or a broader gateway issue.