(self, code: int, reason: str, url: str, method: str, headers: dict, body: str, response, requestHeaders, requestBody)
| 2473 | return {'url': url, 'method': method, 'body': body, 'headers': headers} |
| 2474 | |
| 2475 | def handle_errors(self, code: int, reason: str, url: str, method: str, headers: dict, body: str, response, requestHeaders, requestBody): |
| 2476 | if (code == 418) or (code == 429): |
| 2477 | raise DDoSProtection(self.id + ' ' + str(code) + ' ' + reason + ' ' + body) |
| 2478 | # error response in a form: {"code": -1013, "msg": "Invalid quantity."} |
| 2479 | # following block cointains legacy checks against message patterns in "msg" property |
| 2480 | # will switch "code" checks eventually, when we know all of them |
| 2481 | if code >= 400: |
| 2482 | if body.find('Price * QTY is zero or less') >= 0: |
| 2483 | raise InvalidOrder(self.id + ' order cost = amount * price is zero or less ' + body) |
| 2484 | if body.find('LOT_SIZE') >= 0: |
| 2485 | raise InvalidOrder(self.id + ' order amount should be evenly divisible by lot size ' + body) |
| 2486 | if body.find('PRICE_FILTER') >= 0: |
| 2487 | raise InvalidOrder(self.id + ' order price is invalid, i.e. exceeds allowed price precision, exceeds min price or max price limits or is invalid value in general, use self.price_to_precision(symbol, amount) ' + body) |
| 2488 | if response is None: |
| 2489 | return None # fallback to default error handler |
| 2490 | # check success value for wapi endpoints |
| 2491 | # response in format {'msg': 'The coin does not exist.', 'success': True/false} |
| 2492 | success = self.safe_bool(response, 'success', True) |
| 2493 | if not success: |
| 2494 | messageInner = self.safe_string(response, 'msg') |
| 2495 | parsedMessage = None |
| 2496 | if messageInner is not None: |
| 2497 | try: |
| 2498 | parsedMessage = json.loads(messageInner) |
| 2499 | except Exception as e: |
| 2500 | # do nothing |
| 2501 | parsedMessage = None |
| 2502 | if parsedMessage is not None: |
| 2503 | response = parsedMessage |
| 2504 | message = self.safe_string(response, 'msg') |
| 2505 | if message is not None: |
| 2506 | self.throw_exactly_matched_exception(self.exceptions['exact'], message, self.id + ' ' + message) |
| 2507 | self.throw_broadly_matched_exception(self.exceptions['broad'], message, self.id + ' ' + message) |
| 2508 | # checks against error codes |
| 2509 | error = self.safe_string(response, 'code') |
| 2510 | if error is not None: |
| 2511 | # https://github.com/ccxt/ccxt/issues/6501 |
| 2512 | # https://github.com/ccxt/ccxt/issues/7742 |
| 2513 | if (error == '200') or Precise.string_equals(error, '0'): |
| 2514 | return None |
| 2515 | # a workaround for {"code":-2015,"msg":"Invalid API-key, IP, or permissions for action."} |
| 2516 | # despite that their message is very confusing, it is raised by Binance |
| 2517 | # on a temporary ban, the API key is valid, but disabled for a while |
| 2518 | if (error == '-2015') and self.options['hasAlreadyAuthenticatedSuccessfully']: |
| 2519 | raise DDoSProtection(self.id + ' ' + body) |
| 2520 | feedback = self.id + ' ' + body |
| 2521 | if message == 'No need to change margin type.': |
| 2522 | # not an error |
| 2523 | # https://github.com/ccxt/ccxt/issues/11268 |
| 2524 | # https://github.com/ccxt/ccxt/pull/11624 |
| 2525 | # POST https://fapi.binance.com/fapi/v1/marginType 400 Bad Request |
| 2526 | # binanceusdm {"code":-4046,"msg":"No need to change margin type."} |
| 2527 | raise MarginModeAlreadySet(feedback) |
| 2528 | self.throw_exactly_matched_exception(self.exceptions['exact'], error, feedback) |
| 2529 | raise ExchangeError(feedback) |
| 2530 | if not success: |
| 2531 | raise ExchangeError(self.id + ' ' + body) |
| 2532 | return None |
nothing calls this directly
no test coverage detected