unicity-aos/aos-ce · warning
grant pending read error for capsule_id
Error message
grant pending read error for capsule_id '{capsule_id}', failing closed: {e} What it means
This is a warning log emitted in `take_grant_pending` when reading a pending grant record from the key-value store for a capsule fails (kv::take/get returns Err). The broker deliberately 'fails closed': it treats the unknown state as if no pending grant exists (returns false), so the operation is denied or the caller is re-prompted rather than silently approved. The log is diagnostic — the error value `e` carries the underlying storage failure.
Solutions
- Inspect the underlying error `e` in the log to identify the kv backend failure (IO, lock, deserialization) and fix that root cause.
- Check kv store health/integrity (e.g. reopen or repair the store) and verify the capsule's pending-grant key can be read.
- Confirm no concurrent broker process is contending for the same grant record; ensure single-writer or proper locking.
- If the record is corrupt, delete the stale pending-grant key so the capsule can re-request a fresh grant (fail-closed means the user will be prompted again).
Defensive patterns
Strategy: fallback
Validate before calling
// Before relying on take_grant_pending, probe readability:
fn grant_pending_readable(capsule_id: &str) -> bool {
match kv::get_bytes_opt(&grant_pending_key(capsule_id)) {
Ok(_) => true,
Err(e) => { log::warn!("grant store unreadable: {e}"); false }
}
} Try / catch
match kv::take_bytes(&key) {
Ok(Some(b)) => parse(b),
Ok(None) => /* no pending grant */,
Err(e) => { log::warn!("failing closed: {e}"); /* treat as no grant, deny */ }
} Prevention
- Run health checks on the kv backend before processing grant traffic.
- Monitor warn logs for repeated 'grant pending read error' — recurring failures indicate backend corruption.
- Use atomic, single-writer access to grant records to avoid torn reads.
When it happens
Trigger: Calling take_grant_pending(capsule_id) when the underlying kv backend returns Err on the read/take of the pending-grant key — e.g. storage corruption, backend I/O failure, lock contention, or a malformed record the store cannot deserialize.
Common situations: KV store backend temporarily unavailable or restarting; corrupted grant record left by a crashed broker; permission or IO errors on the embedded store; concurrent take by another broker instance that leaves the record in a bad state.
Understand the failure class
Background: Database query failed: Internal Server Error 500s wrapping SQL, Prisma, and connection failures — what to check first — this error's family across 16 libraries.
Related errors
- grant decision read error for capsule_id
- capability check failed for
- ingress pending read error for source_id
- grant pending read error for capsule_id
AI-assisted analysis of unicity-aos/aos-ce@f6f22024fb (2026-09-13).
Data as JSON: /api/errors/51800cdbd93a5358.
Report an issue: GitHub.
Appendix: source
Thrown at crates/aos-mcp-broker/src/execute.rs:588
/// confirmed-present marker is logged but reported as consumed.
pub(crate) fn take_grant_pending(principal: &str, capsule_id: &str) -> bool {
let Some(key) = grant_pending_key(principal, capsule_id) else {
return false;
};
match kv::get_bytes_opt(&key) {
Ok(Some(_)) => {
if let Err(e) = kv::delete(&key) {
log::warn(format!(
"{}: failed to clear grant pending marker for capsule_id \
'{capsule_id}': {e}",
crate::profile::log_tag()
));
}
true
}
Ok(None) => false,
Err(e) => {
log::warn(format!(
"{}: grant pending read error for capsule_id '{capsule_id}', \
failing closed: {e}",
crate::profile::log_tag()
));
false
}
}
}
/// Confused-deputy guard for state-mutating broker calls.
///
/// `source_id` is the kernel-set UUID of the capsule that originated the
/// inbound IPC message ([`astrid_sdk::runtime::caller`] →
/// `CallerContext::source_id`). It is NOT guest-settable — the kernel
/// stamps it from the publishing capsule's invocation context, so a
/// malicious guest cannot forge it the way it could forge a body field.
/// An ingress is trusted iff the per-(principal, source_id) KV key
/// `mcp.ingress.trust.<source_id>` exists — written ONLY byView on GitHub (pinned to f6f22024fb)