{"record":{"id":"d94bbf646c023b2b","repo":"zeroclaw-labs/zeroclaw","slug":"nevis-introspection-returned-http","errorCode":null,"errorMessage":"Nevis introspection returned HTTP {}","messagePattern":"Nevis introspection returned HTTP (.+?)","errorType":"http","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/zeroclaw-runtime/src/security/nevis.rs","lineNumber":190,"sourceCode":"\n        let mut form = vec![(\"token\", token), (\"client_id\", &self.client_id)];\n        // client_secret is optional (public clients don't need it)\n        let secret_ref;\n        if let Some(ref secret) = self.client_secret {\n            secret_ref = secret.as_str();\n            form.push((\"client_secret\", secret_ref));\n        }\n\n        let resp = self\n            .http_client\n            .post(&introspect_url)\n            .form(&form)\n            .send()\n            .await\n            .context(\"Failed to reach Nevis introspection endpoint\")?;\n\n        if !resp.status().is_success() {\n            bail!(\n                \"Nevis introspection returned HTTP {}\",\n                resp.status().as_u16()\n            );\n        }\n\n        let body: IntrospectionResponse = resp\n            .json()\n            .await\n            .context(\"Failed to parse Nevis introspection response\")?;\n\n        if !body.active {\n            bail!(\"Token is not active (revoked or expired)\");\n        }\n\n        let user_id = body\n            .sub\n            .filter(|s| !s.trim().is_empty())\n            .context(\"Token has missing or empty `sub` claim\")?;","sourceCodeStart":172,"sourceCodeEnd":208,"githubUrl":"https://github.com/zeroclaw-labs/zeroclaw/blob/88bb9c8533fc57ed7a03e36ca7c9ed2bf8336dcc/crates/zeroclaw-runtime/src/security/nevis.rs#L172-L208","documentation":"In remote mode the provider POSTs to {instance_url}/auth/realms/{realm}/protocol/openid-connect/token/introspect (nevis.rs:167-194). The endpoint answered with a non-2xx status. Typical causes: 401 wrong client_id/client_secret, 404 wrong realm or instance_url path, 5xx IdP or reverse-proxy outage.","triggerScenarios":"validate_token with TokenValidationMode::Remote when the Nevis client credentials are wrong, the realm name is misspelled, instance_url has a wrong prefix, or the IdP/proxy returns 502/503.","commonSituations":"Client secret rotated on the IdP but not in ZeroClaw config; staging instance_url pasted into prod config; realm 'master' used instead of the actual realm; firewall or mTLS rules blocking the introspection path.","solutions":["Verify client_id and client_secret match a client registered in the same Nevis realm (HTTP 401 is the tell)","Check instance_url and realm — provider.instance_url() and provider.realm() expose what was configured; 404 usually means one is wrong","Run provider.health_check() to confirm the realm root is reachable at all","For 5xx statuses, retry with backoff and a circuit breaker instead of failing every request"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"if let Err(e) = provider.health_check().await {\n    tracing::warn!(error = %e, \"nevis unreachable at startup; token validation may fail\");\n}","typeGuard":null,"tryCatchPattern":"Parse the trailing HTTP code from the message: 401/403 -> fail fast with a config-error alert (credentials); 404 -> alert on realm/URL mismatch; 5xx -> retry with exponential backoff behind a circuit breaker and emit 503 to callers.","preventionTips":["Run provider.health_check() at startup so credential and realm mistakes surface at boot, not first request","Keep a runbook mapping introspection 401/404/5xx to config vs outage causes","Alert on repeated introspection 5xx as an IdP outage, not as per-user auth failures"],"tags":["auth","nevis","oauth","introspection","http-status","rust"],"backgroundTag":"oauth-introspection-failed","analyzedSha":"88bb9c8533fc57ed7a03e36ca7c9ed2bf8336dcc","analyzedAt":"2026-08-23T01:07:41.857Z","schemaVersion":2},"datasetVersion":"2026-08-23T08:06:27.607Z"}