Handle order rejections
Rejections happen at two points: at placement (place_order() raises OrderException immediately) or after placement (order enters the book then gets rejected by the exchange/RMS, status becomes REJECTED with status_message populated).
Common rejection reasons and what they mean
status_message (paraphrased) | Cause | Fix |
|---|---|---|
| "Insufficient funds" | Not enough margin | Re-check margins (ch 12) before placing, add buffer |
| "RMS:Margin Exceeds" | Same, RMS-level block | Same |
| "Price out of range" / "circuit limit" | Order price outside today's circuit band | Fetch circuit limits from quote data before pricing (ch 77) |
| "Trigger price out of range" | SL/SL-M trigger too close to LTP | Widen trigger distance (ch 51) |
| "Quantity freeze limit exceeded" | Order size exceeds exchange per-order cap | Split into multiple orders (ch 63) |
| "Invalid tradingsymbol" | Symbol/exchange mismatch, or stale instrument token | Re-resolve from a freshly cached instrument master (ch 21) |
| "Market is closed" | Trying to place regular variety outside market hours | Use amo variety instead (ch 62) |
Catch both failure points explicitly
from kiteconnect.exceptions import OrderException
def place_order_safely(kite, **order_params) -> dict:
try:
order_id = kite.place_order(**order_params)
except OrderException as e:
logging.error(f"Order rejected at placement: {e}")
return {"success": False, "reason": str(e), "order_id": None}
result = poll_order_status(kite, order_id)
if result["status"] == "REJECTED":
logging.error(f"Order rejected post-placement: {result['status_message']}")
return {"success": False, "reason": result["status_message"], "order_id": order_id}
return {"success": True, "reason": None, "order_id": order_id}
Never silently swallow a rejection
A rejection means your strategy's intended action did not happen — if your code catches the exception and moves on without surfacing this (alerting, chapter 91; logging, chapter 89), you get a silent divergence between what your strategy *thinks* its position is and what it *actually* is. This class of bug is one of the most dangerous in unattended trading — always fail loudly on a rejection your bot can't self-correct.