zeroclaw-labs/zeroclaw · error

invalid memory.policy.threat_scan_on_hit value {other:?}; ex

Error message

invalid memory.policy.threat_scan_on_hit value {other:?}; expected reject or block-on-read

What it means

ScannedMemory::on_hit() parses [memory.policy].threat_scan_on_hit: trimmed, lowercased, matched against reject | block-on-read (underscore block_on_read also accepted). It is only consulted when threat_scan is on/strict, so an invalid value surfaces on the first scanned write rather than at config load.

Source

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

            "on" => Ok(ThreatScanMode::On),
            "strict" => Ok(ThreatScanMode::Strict),
            other => anyhow::bail!(
                "invalid memory.policy.threat_scan value {other:?}; expected off, on, or strict"
            ),
        }
    }

    fn on_hit(&self) -> anyhow::Result<OnHit> {
        match self
            .policy
            .threat_scan_on_hit
            .trim()
            .to_ascii_lowercase()
            .as_str()
        {
            "reject" => Ok(OnHit::Reject),
            "block-on-read" | "block_on_read" => Ok(OnHit::BlockOnRead),
            other => anyhow::bail!(
                "invalid memory.policy.threat_scan_on_hit value {other:?}; expected reject or block-on-read"
            ),
        }
    }

    fn scan_scope(&self) -> anyhow::Result<Option<Scope>> {
        Ok(match self.scan_mode()? {
            ThreatScanMode::Off => None,
            ThreatScanMode::On => Some(Scope::On),
            ThreatScanMode::Strict => Some(Scope::Strict),
        })
    }

    /// Scope for read-time re-scanning; `None` disables read filtering.
    fn read_scope(&self) -> anyhow::Result<Option<Scope>> {
        if !self.policy.threat_scan_load_time {
            return Ok(None);
        }

View on GitHub (pinned to 88bb9c8533)

Solutions

  1. Use reject or block-on-read (block_on_read also accepted)
  2. Choose block-on-read if writes must persist and flagged rows should be withheld at recall instead
  3. Restart after the edit
  4. Validate both policy enums at startup

Example fix

# before
[memory.policy]
threat_scan = "strict"
threat_scan_on_hit = "block"

# after
[memory.policy]
threat_scan = "strict"
threat_scan_on_hit = "block-on-read"   # or "reject"
Defensive patterns

Strategy: validation

Validate before calling

let v = cfg.memory.policy.threat_scan_on_hit.trim().to_ascii_lowercase();
anyhow::ensure!(matches!(v.as_str(), "reject" | "block-on-read" | "block_on_read"), "threat_scan_on_hit invalid: {v}");

Type guard

fn is_valid_on_hit(v: &str) -> bool {
    matches!(v.trim().to_ascii_lowercase().as_str(), "reject" | "block-on-read" | "block_on_read")
}

Try / catch

if let Err(e) = memory.store(k, v).await {
    if e.to_string().contains("invalid memory.policy.threat_scan_on_hit value") { fix_config_and_restart(); }
    return Err(e);
}

Prevention

When it happens

Trigger: threat_scan_on_hit = "block", "quarantine" or "reject-on-write" while threat_scan is on/strict; "block on read" with spaces fails (only the hyphen/underscore forms pass).

Common situations: Guessing sibling names of documented values; config migrated from another policy schema.

Understand the failure class

Background: "Invalid value" and "allowed values are" config errors: what your library rejected and how to fix it — this error's family across 41 libraries.

Related errors


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