nautechsystems/nautilus_trader · error · anyhow::Error

Failed to repay spot borrow (with conversion) for {coin}: {e

Error message

Failed to repay spot borrow (with conversion) for {coin}: {e}

What it means

The repay-with-conversion wrapper calls the inner repay endpoint (which may convert other assets to cover the borrow) and wraps request failures with a coin-labeled anyhow error. The {e} inside carries the true cause: network failure, Bybit error code (insufficient balance, invalid coin, permissions), or rate limiting. On success, ensure_repay_accepted still validates the result status.

Source

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

    /// # 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 balance for repayment.
    pub async fn repay_spot_borrow_with_conversion(
        &self,
        coin: &str,
        amount: Option<Quantity>,
    ) -> anyhow::Result<BybitRepayResponse> {
        let amount_str = amount.as_ref().map(|q| q.to_string());
        let response = self
            .inner
            .repay(Some(coin), amount_str.as_deref())
            .await
            .map_err(|e| {
                anyhow::anyhow!("Failed to repay spot borrow (with conversion) for {coin}: {e}")
            })?;
        Self::ensure_repay_accepted(coin, response.result.result_status)?;
        Ok(response)
    }

    fn ensure_repay_accepted(coin: &str, status: BybitRepayStatus) -> anyhow::Result<()> {
        anyhow::ensure!(
            status != BybitRepayStatus::Failed,
            "Bybit repay for {coin} returned result status {status}"
        );
        Ok(())
    }

    /// Generate SPOT position reports from wallet balances.
    ///
    /// # Errors
    ///
    /// Returns an error if:

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Read the inner {e} to identify the actual failure code
  2. Confirm the coin has an outstanding borrow and sufficient assets (or convertible balance) exist
  3. Ensure API credentials include repay/margin trading permissions
  4. Back off and retry on transient errors; respect Bybit rate limits

Example fix

// before
client.repay(Some("USDT"), Some("50".into())).await?;
// after
match client.repay(Some("USDT"), Some("50".into())).await {
    Ok(resp) => log::info!("repay accepted"),
    Err(e) => log::error!("repay with conversion failed: {e}"),
}
Defensive patterns

Strategy: try-catch

Validate before calling

let convertible = client.get_convertible_balance(coin).await?;
if convertible < amount { return Err("insufficient assets for convert-repay".into()); }

Try / catch

match client.repay(Some(coin), amount).await {
    Ok(_) => info!("convert-repay {coin} accepted"),
    Err(e) => {
        warn!("convert-repay {coin} failed: {e}");
        sleep_backoff().await;
    }
}

Prevention

When it happens

Trigger: Calling the conversion repay when the account has no convertible assets, the coin has no outstanding borrow, the request times out, or Bybit rejects due to permissions or risk state.

Common situations: Closing short spot positions and cleaning up borrows across multiple coins; margin liquidations leaving unexpected borrow states; automated repay loops hitting rate limits.

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/c73efba9e8b1d058. Report an issue: GitHub.