unicity-aos/aos-ce · warning

grant decision read error for capsule_id

Error message

grant decision read error for capsule_id '{capsule_id}', will prompt: {e}

What it means

This warning is logged in `recorded_grant_decision` when reading a previously recorded grant decision for a capsule from the kv store fails with Err. The function degrades gracefully to None, meaning the broker will treat the decision as unknown and prompt the user again instead of reusing a possibly stale or wrong decision. It is not a hard failure — access is not granted without an explicit decision.

Solutions

  1. Check the underlying error `e` in the log and repair the kv backend (disk full, permissions, corrupted file).
  2. Delete the unreadable grant-decision key so a fresh decision can be recorded on the next prompt.
  3. Verify the store is not being written concurrently by another process with an incompatible format/version.
  4. Accept the safe default: the user will be re-prompted; only investigate further if prompts repeat unexpectedly.
Defensive patterns

Strategy: fallback

Validate before calling

// Pre-check the decision record before trusting it:
fn decision_readable(capsule_id: &str) -> bool {
    kv::get_bytes_opt(&grant_decision_key(capsule_id)).is_ok()
}

Try / catch

match kv::get_bytes_opt(&key) {
    Ok(Some(bytes)) => parse_grant_decision(&bytes).unwrap_or(None),
    Ok(None) => None,
    Err(e) => { log::warn!("will prompt: {e}"); None /* re-prompt user */ }
}

Prevention

When it happens

Trigger: Calling recorded_grant_decision(capsule_id) when kv::get_bytes_opt returns Err for the grant-decision key — storage backend error, corrupted bytes that cannot even be read, or backend unavailable.

Common situations: Embedded kv store IO failure or being reopened mid-request; corrupted decision bytes from an interrupted write; environment (container, tmpfs) where the store file is unreadable; version migration changing key layout.

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


AI-assisted analysis of unicity-aos/aos-ce@f6f22024fb (2026-09-13). Data as JSON: /api/errors/04f2dbaf88a62e9b. Report an issue: GitHub.

Appendix: source

Thrown at crates/aos-mcp-broker/src/grant_decision.rs:187

            crate::profile::log_tag()
        ));
    }
}

/// Read the durable recorded grant decision for `capsule_id`, or `None` if the
/// user has not decided (or the record cannot be read / parsed).
///
/// Fail toward prompting: a missing key, a KV read error, or an unparseable
/// value all return `None` so the broker surfaces a fresh consent prompt. It
/// NEVER returns `Approve` on a read it is unsure about — an auto-approve must
/// only ever follow a record the user actually created.
pub(crate) fn recorded_grant_decision(capsule_id: &str) -> Option<GrantDecision> {
    let key = grant_decision_key(capsule_id)?;
    match kv::get_bytes_opt(&key) {
        Ok(Some(bytes)) => parse_grant_decision(&bytes),
        Ok(None) => None,
        Err(e) => {
            log::warn(format!(
                "{}: grant decision read error for capsule_id '{capsule_id}', \
                 will prompt: {e}",
                crate::profile::log_tag()
            ));
            None
        }
    }
}

/// Map a recorded-decision read to the broker's [`GrantAction`].
///
/// Pure — the KV read happens in [`recorded_grant_decision`]; this is the
/// testable decision spine (a recorded approve auto-responds, a recorded deny
/// suppresses, no record prompts).
pub(crate) fn grant_action(decision: Option<GrantDecision>) -> GrantAction {
    match decision {
        Some(GrantDecision::Approve) => GrantAction::AutoApprove,
        Some(GrantDecision::Deny) => GrantAction::AutoDeny,

View on GitHub (pinned to f6f22024fb)