zeroclaw-labs/zeroclaw · error

memory write denied by policy: {error}

Error message

memory write denied by policy: {error}

What it means

After content screening, enforce_policy() runs policy_gate::validate_store with a PolicyEnforcer built from [memory.policy] (namespace/category rules). Any violation aborts the write; the inner {error} text names the exact rule that failed. Fail-closed by design — every store* variant routes through this check.

Source

Thrown at crates/zeroclaw-memory/src/scanned.rs:244

        category: &MemoryCategory,
    ) -> anyhow::Result<()> {
        let namespace = namespace.unwrap_or("default");
        let enforcer = PolicyEnforcer::new(&self.policy);
        if let Err(error) =
            crate::policy_gate::validate_store(&self.inner, &enforcer, namespace, category).await
        {
            ::zeroclaw_log::record!(
                WARN,
                ::zeroclaw_log::Event::new(module_path!(), ::zeroclaw_log::Action::Note)
                    .with_outcome(::zeroclaw_log::EventOutcome::Failure)
                    .with_attrs(::serde_json::json!({
                        "key": key,
                        "namespace": namespace,
                        "error": error.to_string(),
                    })),
                "memory write denied by policy"
            );
            anyhow::bail!("memory write denied by policy: {error}");
        }
        Ok(())
    }

    /// Re-scan one recalled entry; `true` means the entry passes.
    fn entry_passes_read_scan(&self, entry: &MemoryEntry, scope: Scope) -> bool {
        let findings = threat::scan(&entry.content, scope);
        if findings.is_empty() {
            return true;
        }
        let kinds = findings
            .iter()
            .map(|finding| finding.kind.to_string())
            .collect::<Vec<_>>()
            .join(",");
        ::zeroclaw_log::record!(
            WARN,
            ::zeroclaw_log::Event::new(module_path!(), ::zeroclaw_log::Action::Note)

View on GitHub (pinned to 88bb9c8533)

Solutions

  1. Read the inner error text — it names the violated rule (disallowed namespace, disallowed category, or a limit)
  2. Write to a namespace/category permitted by [memory.policy], or extend the policy rules
  3. If a per-namespace limit tripped, forget old entries or raise the limit
  4. Restart after policy edits so ScannedMemory picks up the new policy
Defensive patterns

Strategy: validation

Validate before calling

// Mirror [memory.policy] rules at the call site: refuse locally before the doomed write
fn policy_allows(policy: &MemoryPolicyConfig, namespace: &str, category: &MemoryCategory) -> bool {
    // replicate the namespace/category allow rules and limits you configured
    true
}
anyhow::ensure!(policy_allows(&cfg.memory.policy, ns, &cat), "write to {ns} will be denied by policy");

Type guard

fn is_policy_denied(e: &anyhow::Error) -> bool {
    e.to_string().starts_with("memory write denied by policy")
}

Try / catch

match memory.store(k, v).await {
    Err(e) if is_policy_denied(&e) => { notify_owner_of_rule(&e); Ok(()) } // the inner text names the violated rule
    other => other,
}

Prevention

When it happens

Trigger: Storing into a namespace or category that [memory.policy] disallows (allow/deny rules, per-namespace limits such as max entries); all store, store_with_metadata, store_with_options and store_with_agent calls are gated.

Common situations: Tightening policy in config and forgetting call sites that write to now-restricted namespaces; new categories missing from the policy allowlist; bulk imports tripping per-namespace quotas.

Related errors


AI-assisted analysis of zeroclaw-labs/zeroclaw@88bb9c8533 (2026-08-23). Data as JSON: /api/errors/42f211f60dc53932. Report an issue: GitHub.