nautechsystems/nautilus_trader · error · anyhow::Error

Failed to borrow {amount} {coin}: {e}

Error message

Failed to borrow {amount} {coin}: {e}

What it means

The high-level BybitHttpClient::borrow() wrapper calls the inner borrow endpoint (spot UTA borrow) and wraps any transport/request error into an anyhow error prefixed with the amount and coin, so callers can see which borrow operation failed. The inner {e} contains the actual cause (HTTP error, Bybit error code, auth failure, insufficient borrowable balance, etc.).

Source

Thrown at crates/adapters/bybit/src/http/client.rs:2337

    /// - `coin`: The coin to repay (e.g., "BTC", "ETH")
    /// - `amount`: Optional amount to borrow. If None, repays all outstanding borrows.
    ///
    /// # Errors
    ///
    /// Returns an error if:
    /// - Credentials are missing.
    /// - The request fails.
    /// - Insufficient collateral for the borrow.
    pub async fn borrow_spot(
        &self,
        coin: &str,
        amount: Quantity,
    ) -> anyhow::Result<BybitBorrowResponse> {
        let amount_str = amount.to_string();
        self.inner
            .borrow(coin, &amount_str)
            .await
            .map_err(|e| anyhow::anyhow!("Failed to borrow {amount} {coin}: {e}"))
    }

    /// Repays spot borrows for a specific coin.
    ///
    /// This should be called after closing short spot positions to avoid accruing interest.
    ///
    /// # Parameters
    ///
    /// - `coin`: The coin to repay (e.g., "BTC", "ETH")
    /// - `amount`: Optional amount to repay. If None, repays all outstanding borrows.
    ///
    /// # Errors
    ///
    /// Returns an error if:
    /// - Credentials are missing.
    /// - The request fails.
    /// - Called during the hourly interest-calculation window (mm:04:00-mm:05:30 UTC each hour).
    /// - Insufficient spot balance for repayment.

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Read the inner error {e} in the message for the actual Bybit/HTTP cause
  2. Verify the coin is borrowable and the amount is within available borrow quota (check margin account data first)
  3. Confirm the API key has margin/borrow permissions enabled on Bybit
  4. Retry only if the error is transient (network/5xx); handle quota errors by reducing size or funding the position instead

Example fix

// before
let resp = client.borrow("USDT", qty).await?; // panics on quota errors
// after
match client.borrow("USDT", qty).await {
    Ok(resp) => { /* use resp */ }
    Err(e) => { log::error!("borrow failed: {e}"); /* reduce size or abort */ }
}
Defensive patterns

Strategy: try-catch

Validate before calling

let quota = client.get_borrow_quota(coin).await?;
if quota < amount {
    return Err(format!("insufficient borrow quota for {coin}: {quota} < {amount}"));
}

Try / catch

match client.borrow(coin, qty).await {
    Ok(resp) => Ok(resp),
    Err(e) => {
        log::error!("spot borrow {coin} failed: {e}");
        if is_transient(&e) { retry_with_backoff().await } else { Err(e) }
    }
}

Prevention

When it happens

Trigger: Calling borrow() when the account lacks borrow quota, the coin is not borrowable on Bybit spot margin, credentials are invalid, or the HTTP request fails (network down, timeout, 5xx).

Common situations: Automated strategies that borrow to fund short spot positions; borrowing during high volatility when Bybit rejects due to risk limits; expired API key permissions missing the 'Spot' trade/UTA margin scope.

Understand the failure class

Background: "API request failed": what wrapped HTTP errors from external APIs mean and how to find the real cause — this error's family across 29 libraries.

Related errors


AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08). Data as JSON: /api/errors/2339e71e0abede8b. Report an issue: GitHub.