{"record":{"id":"f79b70bc4deb60ef","repo":"clockworklabs/SpacetimeDB","slug":"no-kid-found","errorCode":null,"errorMessage":"No kid found","messagePattern":"No kid found","errorType":"exception","errorClass":"TokenValidationError::Other","httpStatus":null,"severity":"error","filePath":"crates/core/src/auth/token_validation.rs","lineNumber":303,"sourceCode":"impl TokenValidator for JwksValidator {\n    async fn validate_token(&self, token: &str) -> Result<SpacetimeIdentityClaims, TokenValidationError> {\n        let header = decode_header(token)?;\n        if let Some(kid) = header.kid {\n            let key = self\n                .keyset\n                .keys\n                .get(&kid)\n                .ok_or_else(|| TokenValidationError::KeyIDNotFound)?;\n            let validator = BasicTokenValidator {\n                public_key: key.decoding_key.clone(),\n                issuer: Some(self.issuer.clone()),\n            };\n            return validator.validate_token(token).await;\n        }\n        log::debug!(\"No key id in header. Trying all keys.\");\n        // TODO: Consider returning an error if no kid is given?\n        // For now, lets just try all the keys.\n        let mut last_error = TokenValidationError::Other(anyhow::anyhow!(\"No kid found\"));\n        for (kid, key) in &self.keyset.keys {\n            log::debug!(\"Trying key {kid}\");\n            let validator = BasicTokenValidator {\n                public_key: key.decoding_key.clone(),\n                issuer: Some(self.issuer.clone()),\n            };\n            match validator.validate_token(token).await {\n                Ok(claims) => return Ok(claims),\n                Err(e) => {\n                    last_error = e;\n                    log::debug!(\"Validating with key {kid} failed\");\n                    continue;\n                }\n            }\n        }\n        // None of the keys worked.\n        Err(last_error)\n    }","sourceCodeStart":285,"sourceCodeEnd":321,"githubUrl":"https://github.com/clockworklabs/SpacetimeDB/blob/524b4487d949b61a07d4f39c862d1290259dfd20/crates/core/src/auth/token_validation.rs#L285-L321","documentation":"When a JWT header carries no kid (key id), the validator falls back to trying every key in the issuer's keyset, and 'No kid found' is the seed error for that loop. Because each failed per-key attempt overwrites it, this message survives to the caller mainly when the keyset is empty — i.e. a kid-less token met an issuer that published no usable keys.","triggerScenarios":"Authenticating with a JWT whose header omits kid against an issuer whose JWKS returned an empty or unusable keyset; custom token-minting scripts that do not set the kid header; a provider publishing its keys array without kid attributes.","commonSituations":"Hand-built JWTs from scripts or tests; misconfigured OIDC providers; partially failed keyset fetches leaving an empty cache entry.","solutions":["Mint tokens so the JOSE header includes the kid of the signing key (standard behavior of major IdPs).","Check the issuer's JWKS endpoint actually returns keys: `curl <jwks_uri>`.","If you operate the IdP, publish keys with kid and ES256 as expected.","Use the normal login flow so the provider generates well-formed tokens."],"exampleFix":"// token header (before)\n{ \"alg\": \"ES256\", \"typ\": \"JWT\" }\n// after\n{ \"alg\": \"ES256\", \"typ\": \"JWT\", \"kid\": \"key-2026-01\" }","handlingStrategy":"validation","validationCode":"import { decodeProtectedHeader } from 'jose';\nconst header = decodeProtectedHeader(jwt);\nif (!header.kid) {\n  throw new Error('token header must include kid — mint it via the provider/login flow, not by hand');\n}","typeGuard":null,"tryCatchPattern":"try {\n  claims = await validate(jwt);\n} catch (e) {\n  if (String(e).includes('No kid found')) {\n    throw new Error('token lacks kid and issuer keyset had no usable keys — check JWKS endpoint and token minting');\n  }\n  throw e;\n}","preventionTips":["Use the official login flow or a mature JWT library that sets kid automatically.","Verify the IdP publishes keys with kid and ES256.","Health-check the JWKS endpoint whenOIDC issues are suspected."],"tags":["spacetimedb","jwt","jwks","kid","oidc"],"backgroundTag":"jwt-missing-key-id","analyzedSha":"524b4487d949b61a07d4f39c862d1290259dfd20","analyzedAt":"2026-08-16T23:58:54.611Z","schemaVersion":2},"datasetVersion":"2026-08-17T04:17:16.089Z"}