{"record":{"id":"eaef8e84d4dd493a","repo":"nautechsystems/nautilus_trader","slug":"failed-to-build-bybitborrowparams","errorCode":null,"errorMessage":"Failed to build BybitBorrowParams","messagePattern":"Failed to build BybitBorrowParams","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/adapters/bybit/src/http/client.rs","lineNumber":1386,"sourceCode":"    /// - Insufficient collateral for the borrow.\n    ///\n    /// # Panics\n    ///\n    /// Panics if the parameter builder fails (should never happen with valid inputs).\n    ///\n    /// # References\n    ///\n    /// - <https://bybit-exchange.github.io/docs/v5/account/borrow>\n    pub async fn borrow(\n        &self,\n        coin: &str,\n        amount: &str,\n    ) -> Result<BybitBorrowResponse, BybitHttpError> {\n        let params = BybitBorrowParamsBuilder::default()\n            .coin(coin.to_string())\n            .amount(amount.to_string())\n            .build()\n            .expect(\"Failed to build BybitBorrowParams\");\n\n        let body = serde_json::to_vec(&params)?;\n        self.send_request::<_, ()>(Method::POST, \"/v5/account/borrow\", None, Some(body), true)\n            .await\n    }\n\n    /// Manually repays borrowed coins without asset conversion.\n    ///\n    /// # Errors\n    ///\n    /// Returns an error if:\n    /// - Credentials are missing.\n    /// - The request fails.\n    /// - Called during the hourly interest-calculation window (mm:04:00-mm:05:30 UTC each hour).\n    /// - Insufficient spot balance for repayment.\n    ///\n    /// # Panics\n    ///","sourceCodeStart":1368,"sourceCodeEnd":1404,"githubUrl":"https://github.com/nautechsystems/nautilus_trader/blob/18893faf8b356be3320add8de2f861b0b647cf06/crates/adapters/bybit/src/http/client.rs#L1368-L1404","documentation":"`borrow` builds `BybitBorrowParams` (coin + amount) and panics via `expect` when `build()` fails, which derive_builder signals only when a required field is missing. Both `coin` and `amount` are set in the chain, so a panic indicates a struct/builder mismatch — typically a new required field or an internal bug.","triggerScenarios":"`BybitBorrowParams` gains an additional required field not set here (e.g. a new mandatory borrow parameter), or the raw builder is used without `coin`/`amount`.","commonSituations":"Post-upgrade schema drift; hand-rolling the builder in custom trading logic and omitting a setter.","solutions":["Verify all non-Option fields of `BybitBorrowParams` are set on the builder","After upgrading, re-check this call site against the updated struct","Replace `expect` with `?` mapping to `BybitHttpError`","Report as a bug if it fires via public `borrow`"],"exampleFix":"// before\n.build().expect(\"Failed to build BybitBorrowParams\");\n// after\n.build().map_err(BybitHttpError::InvalidParams)?;","handlingStrategy":"validation","validationCode":"// coin and amount are required for borrow\nassert!(!coin.is_empty() && !amount.is_empty(), \"coin and amount are required\");\nassert!(amount.parse::<Decimal>().is_ok(), \"amount must be a decimal string\");","typeGuard":null,"tryCatchPattern":"match client.borrow(coin, amount).await { Ok(r) => r, Err(e) => { log::error!(\"borrow failed: {e}\"); return Err(e.into()); } }","preventionTips":["Pass coin and amount explicitly; never rely on defaults","Validate amount parses as Decimal and is > 0","Check borrowable quota via the borrow API before borrowing"],"tags":["panic","builder","borrow","bybit"],"backgroundTag":"internal-invariant-violation","analyzedSha":"18893faf8b356be3320add8de2f861b0b647cf06","analyzedAt":"2026-09-08T20:49:34.690Z","contentChangedAt":"2026-09-08T20:49:34.690Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}