zeroclaw-labs/zeroclaw · error

security.otp.gated_actions[{i}] contains invalid characters:

Error message

security.otp.gated_actions[{i}] contains invalid characters: {normalized}

What it means

Each entry in security.otp.gated_actions is normalized first (the message prints the normalized form), then must contain only ASCII alphanumerics, '_', or '-'. This character check runs before the membership test against default_otp_gated_actions(), so a malformed name fails here and a well-formed but unknown name fails the subsequent known-actions check. Dotted, coloned, or spaced action names are the usual offenders.

Source

Thrown at crates/zeroclaw-config/src/schema.rs:21420

                InvalidNumericRange,
                "security.otp.challenge_max_attempts",
                "security.otp.challenge_max_attempts must be greater than 0"
            );
        }
        for (i, action) in self.security.otp.gated_actions.iter().enumerate() {
            let normalized = action.trim();
            if normalized.is_empty() {
                validation_bail!(
                    RequiredFieldEmpty,
                    format!("security.otp.gated_actions[{i}]"),
                    "security.otp.gated_actions[{i}] must not be empty"
                );
            }
            if !normalized
                .chars()
                .all(|c| c.is_ascii_alphanumeric() || c == '_' || c == '-')
            {
                anyhow::bail!(
                    "security.otp.gated_actions[{i}] contains invalid characters: {normalized}"
                );
            }
            if !default_otp_gated_actions()
                .iter()
                .any(|known| known == normalized)
            {
                ::zeroclaw_log::record!(
                    WARN,
                    ::zeroclaw_log::Event::new(module_path!(), ::zeroclaw_log::Action::Note)
                        .with_outcome(::zeroclaw_log::EventOutcome::Unknown)
                        .with_attrs(::serde_json::json!({
                            "action": normalized,
                            "known_actions": default_otp_gated_actions(),
                        })),
                    "security.otp.gated_actions entry does not match a known gated action and will not be enforced: "
                );
            }

View on GitHub (pinned to 88bb9c8533)

Solutions

  1. Rewrite the entry using only a-z, A-Z, 0-9, '_', '-': "memory_wipe" instead of "memory.wipe"
  2. After fixing the charset, confirm the name is one of default_otp_gated_actions() — unknown but well-formed names fail the very next check
  3. Keep custom action names snake_case or kebab-case to match the existing convention

Example fix

# before
[security.otp]
gated_actions = ["memory.wipe", "shell_exec"]

# after
[security.otp]
gated_actions = ["memory_wipe", "shell_exec"]
Defensive patterns

Strategy: validation

Validate before calling

fn valid_gated_action(action: &str) -> bool {
    action.chars().all(|c| c.is_ascii_alphanumeric() || c == '_' || c == '-')
}

fn gated_actions_precheck(actions: &[String]) -> Result<(), String> {
    if let Some(bad) = actions.iter().find(|a| !valid_gated_action(a)) {
        return Err(format!("invalid gated action charset: {bad:?}"));
    }
    Ok(())
}

Type guard

fn is_sluglike_action(a: &str) -> bool {
    !a.is_empty() && valid_gated_action(a)
}

Try / catch

if let Err(err) = config.validate() {
    if err.to_string().contains("security.otp.gated_actions") {
        // take the index from the message and slugify that entry (dots/colons/spaces -> '_')
    }
}

Prevention

When it happens

Trigger: Set gated_actions to an entry like "memory.wipe", "shell exec", "ops:restart", or any string containing '.', ':', ' ', '@', '/' after normalization. The bail fires with the entry's index and normalized value.

Common situations: Inventing new gated actions without registering them in the default set; copying action IDs from a dashboard that renders them as `namespace.action`; merging config from another tool whose action IDs contain dots.

Understand the failure class

Related errors


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