nautechsystems/nautilus_trader · error · anyhow::Error

Bybit repay for {coin} returned result status {status}

Error message

Bybit repay for {coin} returned result status {status}

What it means

After a repay request succeeds at the HTTP level, BybitHttpClient::ensure_repay_accepted checks response.result.result_status and fails with anyhow::ensure! if it equals BybitRepayStatus::Failed. Bybit accepts the request asynchronously and reports execution outcome in this status, so a 'Failed' status means the repay did not execute even though the API call returned 200.

Source

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

    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:
    /// - The wallet balance request fails.
    /// - Parsing fails.
    async fn generate_spot_position_reports_from_wallet(
        &self,
        account_id: AccountId,
        instrument_id: InstrumentId,
    ) -> anyhow::Result<Vec<PositionStatusReport>> {

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Query the current borrow/liability state before repaying and repay only the outstanding amount
  2. Treat Failed status as final: do not blind-retry the same amount; re-check balance and rebuild the repay request
  3. Check Bybit's account borrow records endpoint for the reason the repay failed
  4. Idempotency: track in-flight repay operations to avoid duplicate submissions

Example fix

// before
client.no_convert_repay(coin, Some("100".into())).await?; // may return Failed status
// after
let outstanding = client.get_borrow_amount(coin).await?;
if outstanding > 0 {
    client.no_convert_repay(coin, Some(outstanding.to_string().into())).await?;
}
Defensive patterns

Strategy: try-catch

Validate before calling

let outstanding = client.get_outstanding_borrow(coin).await?;
if outstanding == 0 { return Ok(()); } // repay would return Failed status

Try / catch

let resp = client.no_convert_repay(coin, amt).await?;
if resp.result.result_status == BybitRepayStatus::Failed {
    // re-query borrow records and rebuild request; do not blind-retry
}

Prevention

When it happens

Trigger: Repaying a spot borrow where Bybit rejected the repay operation (e.g. borrow already repaid, insufficient assets after conversion, invalid amount), yielding result_status=Failed inside an otherwise successful response.

Common situations: Racing repay calls where the first already cleared the liability; automated cleanup that repays more than outstanding; convert-repay attempts when no assets are convertible.

Related errors


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