{"record":{"id":"ff028d99c37731a3","repo":"nautechsystems/nautilus_trader","slug":"l3-order-book-requires-api-credentials-configure","errorCode":null,"errorMessage":"L3 order book requires API credentials; configure api_key and api_secret","messagePattern":"L3 order book requires API credentials; configure api_key and api_secret","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/adapters/kraken/src/data/spot.rs","lineNumber":308,"sourceCode":"        }\n        let tasks_result = self.finish_tasks().await;\n        self.is_connected.store(false, Ordering::Release);\n        tasks_result?;\n        ws_result?;\n        Ok(ws_l3_result?)\n    }\n\n    fn subscribe_l3_book(&mut self, cmd: &SubscribeBookDeltas) -> anyhow::Result<()> {\n        let instrument_id = cmd.instrument_id;\n        let symbol_ustr = instrument_id.symbol.inner();\n        let depth = cmd.depth.map_or(1000, |d| d.get() as u32);\n\n        if !matches!(depth, 10 | 100 | 1000) {\n            anyhow::bail!(\"Invalid L3 depth {depth} for Kraken Spot, valid values: 10, 100, 1000\");\n        }\n\n        if !self.config.has_api_credentials() {\n            anyhow::bail!(\n                \"L3 order book requires API credentials; configure api_key and api_secret\"\n            );\n        }\n\n        let handler_finished = self\n            .l3_handler_task\n            .as_ref()\n            .is_none_or(TaskRef::is_finished);\n\n        if self.ws_l3.is_none() {\n            let ws_l3 = KrakenSpotWebSocketClient::l3(\n                self.config.clone(),\n                self.cancellation_token.clone(),\n                self.config\n                    .proxy_url\n                    .as_ref()\n                    .map(|value| value.expose_secret().to_owned()),\n            )","sourceCodeStart":290,"sourceCodeEnd":326,"githubUrl":"https://github.com/nautechsystems/nautilus_trader/blob/18893faf8b356be3320add8de2f861b0b647cf06/crates/adapters/kraken/src/data/spot.rs#L290-L326","documentation":"Kraken Spot's L3 order book feed is an authenticated private endpoint, so `subscribe_l3_book` requires API credentials. Without api_key/api_secret in the config the subscription is refused before any network call.","triggerScenarios":"Calling `subscribe_book_deltas` -> `subscribe_l3_book` on a Kraken Spot client whose config has no Spot API credentials (public/market-data-only setup).","commonSituations":"Running a market-data-only node and adding an L3 book subscription; credentials configured for Futures but not Spot; env vars for keys not set in the deployment.","solutions":["Configure `api_key` and `api_secret` in the Kraken Spot client config","Verify the credentials are Spot credentials (not Futures) and are loaded into the environment","Fall back to L2 (depth snapshots) if authenticated access is not available"],"exampleFix":"// before\nlet config = KrakenDataClientConfig { api_key: None, api_secret: None, ..Default::default() };\n// after\nlet config = KrakenDataClientConfig {\n    api_key: Some(read_env(\"KRAKEN_API_KEY\")),\n    api_secret: Some(read_env(\"KRAKEN_API_SECRET\")),\n    ..Default::default()\n};","handlingStrategy":"validation","validationCode":"if !config.has_api_credentials() {\n    return Err(anyhow::anyhow!(\"L3 book requires api_key and api_secret\"));\n}","typeGuard":"fn has_spot_credentials(cfg: &KrakenDataClientConfig) -> bool {\n    cfg.api_key.is_some() && cfg.api_secret.is_some()\n}","tryCatchPattern":"match client.subscribe_book_deltas(cmd) {\n    Err(e) if e.to_string().contains(\"API credentials\") => {\n        log::warn!(\"falling back to L2 book (public feed)\");\n        client.subscribe_book(cmd.into_l2())?;\n    }\n    r => r?,\n}","preventionTips":["Provision Spot API keys in env/config before enabling authenticated subscriptions","Check has_api_credentials() before requesting private data","Distinguish Spot vs Futures credentials; they are not interchangeable"],"tags":["rust","kraken-spot","orderbook","authentication"],"backgroundTag":"missing-credentials","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"}