{"record":{"id":"5a6dee688de1a6d4","repo":"zeroclaw-labs/zeroclaw","slug":"risk-profiles-profile-alias-shell-env-passthroug","errorCode":null,"errorMessage":"risk_profiles.{profile_alias}.shell_env_passthrough[{i}] is invalid ({env_name}); expected [A-Za-z_][A-Za-z0-9_]*","messagePattern":"risk_profiles\\.(.+?)\\.shell_env_passthrough\\[(.+?)\\] is invalid \\((.+?)\\); expected \\[A-Za-z_\\]\\[A-Za-z0-9_\\]\\*","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/zeroclaw-config/src/schema.rs","lineNumber":21366,"sourceCode":"                                \"mcp_bundle\": bundle_alias,\n                            })),\n                            \"agents.<alias>.mcp_bundles references an undefined [mcp_bundles.<alias>]; the agent is granted no servers from it\"\n                        );\n                    }\n                }\n            }\n        }\n\n        // Validate every configured risk profile. Each profile stands on\n        // its own — there is no \"active\" or \"default\" risk profile concept;\n        // an agent's `risk_profile` field names exactly which one applies.\n        let mut profile_aliases: Vec<&String> = self.risk_profiles.keys().collect();\n        profile_aliases.sort();\n        for profile_alias in profile_aliases {\n            let profile = &self.risk_profiles[profile_alias];\n            for (i, env_name) in profile.shell_env_passthrough.iter().enumerate() {\n                if !is_valid_env_var_name(env_name) {\n                    anyhow::bail!(\n                        \"risk_profiles.{profile_alias}.shell_env_passthrough[{i}] is invalid ({env_name}); expected [A-Za-z_][A-Za-z0-9_]*\"\n                    );\n                }\n            }\n        }\n\n        // Security OTP / estop\n        if self.security.otp.challenge_max_attempts == 0 {\n            validation_bail!(\n                InvalidNumericRange,\n                \"security.otp.challenge_max_attempts\",\n                \"security.otp.challenge_max_attempts must be greater than 0\"\n            );\n        }\n        if self.security.otp.token_ttl_secs == 0 {\n            validation_bail!(\n                InvalidNumericRange,\n                \"security.otp.token_ttl_secs\",","sourceCodeStart":21348,"sourceCodeEnd":21384,"githubUrl":"https://github.com/zeroclaw-labs/zeroclaw/blob/88bb9c8533fc57ed7a03e36ca7c9ed2bf8336dcc/crates/zeroclaw-config/src/schema.rs#L21348-L21384","documentation":"Every entry under [risk_profiles.<alias>] shell_env_passthrough must be a bare environment variable name matching [A-Za-z_][A-Za-z0-9_]* (is_valid_env_var_name, schema.rs:12422). Profiles are checked in sorted alias order and the message includes the failing index, so the exact entry is identifiable. Values carrying assignments, hyphens, dots, leading digits, or emptiness cannot be matched against a real process environment and are rejected.","triggerScenarios":"Write shell export syntax into the list (\"PATH=...\"), a hyphenated/dotted name (\"AWS-REGION\", \"aws.region\"), a leading digit (\"1FOO\"), whitespace inside the entry, or an empty string \"\" in any risk profile's shell_env_passthrough array.","commonSituations":"Copy-pasting `export KEY=value` lines into the TOML array; pasting Docker `-e KEY=val` fragments; config generators emitting empty-string defaults; team-shared profile aliases where one author assumed bash semantics.","solutions":["Rewrite the failing entry as a bare name: \"AWS_REGION\" instead of \"AWS_REGION=us-east-1\"","Convert hyphens/dots to underscores (AWS-REGION -> AWS_REGION) and make the first character a letter or underscore","Delete empty-string entries and stray quotes or padding spaces","Fix every profile listed before the one that failed — the loop bails on first offense, so earlier profiles are already clean, later ones may not be"],"exampleFix":"# before\n[risk_profiles.dev]\nshell_env_passthrough = [\"AWS_REGION=us-east-1\", \"npm_config_registry\"]\n\n# after\n[risk_profiles.dev]\nshell_env_passthrough = [\"AWS_REGION\", \"npm_config_registry\"]","handlingStrategy":"validation","validationCode":"fn valid_env_name(name: &str) -> bool {\n    let mut chars = name.chars();\n    match chars.next() {\n        Some(first) if first.is_ascii_alphabetic() || first == '_' => {}\n        _ => return false,\n    }\n    chars.all(|c| c.is_ascii_alphanumeric() || c == '_')\n}\n\nfn passthrough_precheck(cfg: &zeroclaw_config::Config) -> Result<(), String> {\n    for profile in cfg.risk_profiles.values() {\n        if let Some(bad) = profile.shell_env_passthrough.iter().find(|n| !valid_env_name(n)) {\n            return Err(format!(\"invalid shell_env_passthrough entry: {bad:?}\"));\n        }\n    }\n    Ok(())\n}","typeGuard":"fn is_bare_env_var_name(entry: &str) -> bool {\n    !entry.contains('=') && valid_env_name(entry)\n}","tryCatchPattern":"if let Err(err) = config.validate() {\n    if err.to_string().contains(\"shell_env_passthrough\") {\n        // parse alias and index from the message, rewrite that entry as a bare name\n    }\n}","preventionTips":["Store bare names only — never KEY=value pairs or docker -e fragments","Lint risk_profiles in CI with the same [A-Za-z_][A-Za-z0-9_]* regex","Reject empty strings at the config-editor level, not at startup"],"tags":["configuration","validation","environment-variables","risk-profiles","zeroclaw","rust"],"backgroundTag":"invalid-environment-variable-name","analyzedSha":"88bb9c8533fc57ed7a03e36ca7c9ed2bf8336dcc","analyzedAt":"2026-08-23T01:07:41.857Z","schemaVersion":2},"datasetVersion":"2026-08-23T08:06:27.607Z"}