{"record":{"id":"8bb9d5709b8bee4e","repo":"astrid-runtime/astrid","slug":"invalid-device-revocation-key-id","errorCode":null,"errorMessage":"invalid device revocation key id","messagePattern":"invalid device revocation key id","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/astrid-gateway/src/revocations.rs","lineNumber":188,"sourceCode":"    }\n}\n\n/// Record the maximum device revocation epoch durably using the same CAS/max\n/// rule as principal revocations.\n///\n/// A successful CAS, or a successful maximum-epoch fallback write after a\n/// CAS error, leaves a fence that startup hydration can restore. The function\n/// still returns an error after any CAS error so the HTTP caller withholds\n/// `204`, even when the fallback tombstone succeeded. If both writes fail,\n/// there is no durable fence; the caller may install a process-local maximum,\n/// but a later healthy empty KV cannot reconstruct it.\npub async fn record_device_max(\n    store: &dyn KvStore,\n    key_id: &str,\n    epoch: u64,\n) -> anyhow::Result<u64> {\n    if key_id.is_empty() || key_id.contains('/') {\n        anyhow::bail!(\"invalid device revocation key id\");\n    }\n    let key = format!(\"{DEVICE_PREFIX}{key_id}\");\n    loop {\n        let current = store\n            .get(REVOCATION_NAMESPACE, &key)\n            .await\n            .map_err(|error| anyhow::anyhow!(\"read device revocation {key_id}: {error}\"))?;\n        let current_epoch = current\n            .as_deref()\n            .map(|bytes| decode_epoch(bytes, &key))\n            .transpose()?;\n        let wanted = current_epoch.map_or(epoch, |current| current.max(epoch));\n        if current_epoch == Some(wanted) {\n            return Ok(wanted);\n        }\n        match store\n            .compare_and_swap(\n                REVOCATION_NAMESPACE,","sourceCodeStart":170,"sourceCodeEnd":206,"githubUrl":"https://github.com/astrid-runtime/astrid/blob/affd8760f44190dbdfbec23403f4c4b642c33112/crates/astrid-gateway/src/revocations.rs#L170-L206","documentation":"Validation guard in record_device_max: the device revocation key id supplied with the revocation request does not parse as a valid key id, so the maximum-epoch fence cannot be recorded durably. This is an input-shape guard on the revocation record before it is written to the KV store via the CAS/max path; apply_device_revocation surfaces it as a request error.","triggerScenarios":"Thrown at crates/astrid-gateway/src/revocations.rs:188 when the library encounters an invalid state.","commonSituations":"See trigger scenarios.","solutions":["Verify the key id format supplied to the revocation API","Reject/fix the caller producing malformed key ids","Check for KV entries written by older versions with an incompatible key format"],"exampleFix":null,"handlingStrategy":"validation","validationCode":null,"typeGuard":null,"tryCatchPattern":null,"preventionTips":[],"tags":[],"backgroundTag":null,"analyzedSha":"affd8760f44190dbdfbec23403f4c4b642c33112","analyzedAt":"2026-09-09T21:28:12.402Z","contentChangedAt":"2026-09-09T21:28:12.402Z","schemaVersion":2},"datasetVersion":"2026-09-17T15:17:12.973Z"}