{"record":{"id":"cf7328ab431090e8","repo":"nautechsystems/nautilus_trader","slug":"missing-credentials","errorCode":null,"errorMessage":"Missing credentials","messagePattern":"Missing credentials","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/adapters/kraken/src/http/spot/client.rs","lineNumber":307,"sourceCode":"        )])\n    }\n\n    fn rate_limit_keys(endpoint: &str) -> Vec<String> {\n        let normalized = endpoint.split('?').next().unwrap_or(endpoint);\n        let route = format!(\"kraken:spot:{normalized}\");\n        vec![KRAKEN_GLOBAL_RATE_KEY.to_string(), route]\n    }\n\n    fn sign_spot(\n        &self,\n        path: &str,\n        nonce: u64,\n        params: &HashMap<String, String>,\n    ) -> anyhow::Result<(HashMap<String, String>, String)> {\n        let credential = self\n            .credential\n            .as_ref()\n            .ok_or_else(|| anyhow::anyhow!(\"Missing credentials\"))?;\n\n        let (signature, post_data) = credential.sign_spot(path, nonce, params)?;\n\n        let mut headers = HashMap::new();\n        headers.insert(\"API-Key\".to_string(), credential.api_key().to_string());\n        headers.insert(\"API-Sign\".to_string(), signature);\n\n        Ok((headers, post_data))\n    }\n\n    async fn send_request<T: DeserializeOwned>(\n        &self,\n        method: Method,\n        endpoint: &str,\n        body: Option<Vec<u8>>,\n        authenticate: bool,\n    ) -> anyhow::Result<KrakenResponse<T>, KrakenHttpError> {\n        self.send_request_with_body(method, endpoint, RequestBody::Form(body), authenticate)","sourceCodeStart":289,"sourceCodeEnd":325,"githubUrl":"https://github.com/nautechsystems/nautilus_trader/blob/18893faf8b356be3320add8de2f861b0b647cf06/crates/adapters/kraken/src/http/spot/client.rs#L289-L325","documentation":"`sign_spot` signs a private Kraken REST endpoint request, which requires an API key/secret. The client stores its credential in an `Option`, and when it is `None` signing cannot proceed, so this error is returned. It means a private (authenticated) endpoint was called on a client constructed without credentials.","triggerScenarios":"Calling `send_request_attempt` for a private endpoint (e.g. OpenPositions, Balance) on a client that was created without `api_key`/`api_secret`, or with `credential: None`.","commonSituations":"Using the client in public-data-only mode and accidentally calling private endpoints; credential loading failed silently upstream; environment variables for keys not set so `None` was passed; refactored constructor defaults dropping credentials.","solutions":["Construct the client with valid `api_key` and `api_secret` so `credential` is `Some`.","Guard call sites: only invoke private endpoints when credentials are configured; check `client.credential.is_some()` first.","Verify env vars/config that supply the keys are actually loaded before client construction."],"exampleFix":"// before\nlet client = KrakenSpotHttpClient::new(url, \"\", \"\", rps, timeout, None)?;\nclient.request_open_positions(...).await?; // Missing credentials\n// after\nlet client = KrakenSpotHttpClient::new(url, &api_key, &api_secret, rps, timeout, None)?;","handlingStrategy":"try-catch","validationCode":"fn ensure_private_ready(client: &KrakenSpotHttpClient) -> Result<(), String> {\n    if client.credential.is_none() {\n        Err(\"private endpoint called without credentials\".into())\n    } else { Ok(()) }\n}","typeGuard":"fn has_credential(c: &Option<KrakenCredential>) -> bool { c.is_some() }","tryCatchPattern":"match client.request_open_positions(params).await {\n    Ok(reports) => reports,\n    Err(e) if e.to_string().contains(\"Missing credentials\") => {\n        return Err(anyhow::anyhow!(\"configure KRAKEN_API_KEY/KRAKEN_API_SECRET before private calls: {e}\"));\n    }\n    Err(e) => return Err(e),\n}","preventionTips":["Load API keys from env/config and fail fast at startup if absent","Route public-only workloads through a credential-free client and private calls through an authenticated one","Never pass empty strings as key/secret; pass None explicitly so intent is clear"],"tags":["authentication","credentials","kraken","api-key"],"backgroundTag":"missing-credentials","analyzedSha":"18893faf8b356be3320add8de2f861b0b647cf06","analyzedAt":"2026-09-08T20:49:34.690Z","contentChangedAt":"2026-09-08T20:49:34.690Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}