{"record":{"id":"e2e5ebdcc40aad7d","repo":"zeroclaw-labs/zeroclaw","slug":"invalid-memory-policy-redact-categories-value-s","errorCode":null,"errorMessage":"invalid memory.policy.redact_categories value(s): {}; expected secret, api_key, private_key, email, or phone","messagePattern":"invalid memory\\.policy\\.redact_categories value\\(s\\): (.+?); expected secret, api_key, private_key, email, or phone","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/zeroclaw-memory/src/scanned.rs","lineNumber":137,"sourceCode":"    /// Scope for read-time re-scanning; `None` disables read filtering.\n    fn read_scope(&self) -> anyhow::Result<Option<Scope>> {\n        if !self.policy.threat_scan_load_time {\n            return Ok(None);\n        }\n        self.scan_scope()\n    }\n\n    fn redaction_categories(&self) -> anyhow::Result<Vec<RedactCategory>> {\n        let mut parsed = Vec::with_capacity(self.policy.redact_categories.len());\n        let mut invalid = Vec::new();\n        for category in &self.policy.redact_categories {\n            match RedactCategory::from_config(category) {\n                Some(category) => parsed.push(category),\n                None => invalid.push(category.as_str()),\n            }\n        }\n        if !invalid.is_empty() {\n            anyhow::bail!(\n                \"invalid memory.policy.redact_categories value(s): {}; expected secret, api_key, private_key, email, or phone\",\n                invalid.join(\",\")\n            );\n        }\n        Ok(parsed)\n    }\n\n    /// Candidate count to request from the backend for a read-time filtered\n    /// recall of `limit` rows. See [`READ_REFILL_MULTIPLIER`] for the bound.\n    fn read_fetch_limit(limit: usize) -> usize {\n        if limit == 0 {\n            return 0;\n        }\n        limit.saturating_mul(READ_REFILL_MULTIPLIER).max(limit)\n    }\n\n    /// Run the write-boundary pipeline on one content payload: redact\n    /// configured categories (when `redact_on_write` is enabled), then scan","sourceCodeStart":119,"sourceCodeEnd":155,"githubUrl":"https://github.com/zeroclaw-labs/zeroclaw/blob/88bb9c8533fc57ed7a03e36ca7c9ed2bf8336dcc/crates/zeroclaw-memory/src/scanned.rs#L119-L155","documentation":"When redact_on_write is enabled, redaction_categories() maps every entry of [memory.policy].redact_categories through RedactCategory::from_config; accepted values are secret, api_key, private_key, email, phone. All unknown entries are collected and reported together in one error listing the offenders.","triggerScenarios":"redact_on_write = true together with category names like phone_number, credit_card, credentials, or email_address; parsing happens on the write path, so it fails on the first store after enabling redaction.","commonSituations":"Renamed categories across zeroclaw versions; guessed category names; template configs left with placeholder entries.","solutions":["Rename or remove invalid entries so only the five supported categories remain","Leave redact_categories empty (with redact_on_write still true) to disable category redaction without touching the flag","Restart after editing and test with a single store call","Validate the list at startup when redaction is enabled"],"exampleFix":"# before\n[memory.policy]\nredact_on_write = true\nredact_categories = [\"phone_number\", \"emails\"]\n\n# after\n[memory.policy]\nredact_on_write = true\nredact_categories = [\"phone\", \"email\"]   # secret | api_key | private_key | email | phone","handlingStrategy":"validation","validationCode":"const VALID: [&str; 5] = [\"secret\", \"api_key\", \"private_key\", \"email\", \"phone\"];\nfor c in &cfg.memory.policy.redact_categories {\n    anyhow::ensure!(VALID.contains(&c.trim().to_ascii_lowercase().as_str()), \"invalid redact category {c}\");\n}","typeGuard":"fn is_valid_redact_category(v: &str) -> bool {\n    matches!(v, \"secret\" | \"api_key\" | \"private_key\" | \"email\" | \"phone\")\n}","tryCatchPattern":"if let Err(e) = memory.store(k, v).await {\n    if e.to_string().contains(\"invalid memory.policy.redact_categories value\") { fix_config_and_restart(); }\n    return Err(e);\n}","preventionTips":["Validate categories whenever redact_on_write is enabled","Empty list is valid and disables category redaction","Keep category names sourced from the docs table"],"tags":["configuration","policy","redaction","validation","rust"],"backgroundTag":"invalid-config-value","analyzedSha":"88bb9c8533fc57ed7a03e36ca7c9ed2bf8336dcc","analyzedAt":"2026-08-23T01:07:41.857Z","schemaVersion":2},"datasetVersion":"2026-08-23T08:06:27.607Z"}