nautechsystems/nautilus_trader · error

Missing credentials

Error message

Missing credentials

What it means

`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.

Source

Thrown at crates/adapters/kraken/src/http/spot/client.rs:307

        )])
    }

    fn rate_limit_keys(endpoint: &str) -> Vec<String> {
        let normalized = endpoint.split('?').next().unwrap_or(endpoint);
        let route = format!("kraken:spot:{normalized}");
        vec![KRAKEN_GLOBAL_RATE_KEY.to_string(), route]
    }

    fn sign_spot(
        &self,
        path: &str,
        nonce: u64,
        params: &HashMap<String, String>,
    ) -> anyhow::Result<(HashMap<String, String>, String)> {
        let credential = self
            .credential
            .as_ref()
            .ok_or_else(|| anyhow::anyhow!("Missing credentials"))?;

        let (signature, post_data) = credential.sign_spot(path, nonce, params)?;

        let mut headers = HashMap::new();
        headers.insert("API-Key".to_string(), credential.api_key().to_string());
        headers.insert("API-Sign".to_string(), signature);

        Ok((headers, post_data))
    }

    async fn send_request<T: DeserializeOwned>(
        &self,
        method: Method,
        endpoint: &str,
        body: Option<Vec<u8>>,
        authenticate: bool,
    ) -> anyhow::Result<KrakenResponse<T>, KrakenHttpError> {
        self.send_request_with_body(method, endpoint, RequestBody::Form(body), authenticate)

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Construct the client with valid `api_key` and `api_secret` so `credential` is `Some`.
  2. Guard call sites: only invoke private endpoints when credentials are configured; check `client.credential.is_some()` first.
  3. Verify env vars/config that supply the keys are actually loaded before client construction.

Example fix

// before
let client = KrakenSpotHttpClient::new(url, "", "", rps, timeout, None)?;
client.request_open_positions(...).await?; // Missing credentials
// after
let client = KrakenSpotHttpClient::new(url, &api_key, &api_secret, rps, timeout, None)?;
Defensive patterns

Strategy: try-catch

Validate before calling

fn ensure_private_ready(client: &KrakenSpotHttpClient) -> Result<(), String> {
    if client.credential.is_none() {
        Err("private endpoint called without credentials".into())
    } else { Ok(()) }
}

Type guard

fn has_credential(c: &Option<KrakenCredential>) -> bool { c.is_some() }

Try / catch

match client.request_open_positions(params).await {
    Ok(reports) => reports,
    Err(e) if e.to_string().contains("Missing credentials") => {
        return Err(anyhow::anyhow!("configure KRAKEN_API_KEY/KRAKEN_API_SECRET before private calls: {e}"));
    }
    Err(e) => return Err(e),
}

Prevention

When it happens

Trigger: 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`.

Common situations: 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.

Related errors


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