{"record":{"id":"9a88b229d65d839c","repo":"Hmbown/CodeWhale","slug":"invalid-lane-environment-key-key","errorCode":null,"errorMessage":"invalid lane environment key {key:?}","messagePattern":"invalid lane environment key (.+?)","errorType":"validation","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/lane/src/runtime.rs","lineNumber":234,"sourceCode":"    match std::fs::remove_file(path) {\n        Ok(()) => Ok(()),\n        Err(err) if err.kind() == std::io::ErrorKind::NotFound => Ok(()),\n        Err(err) => Err(err).with_context(|| format!(\"remove {}\", path.display())),\n    }\n}\n\nfn valid_environment_key(key: &str) -> bool {\n    let mut chars = key.chars();\n    chars\n        .next()\n        .is_some_and(|ch| ch == '_' || ch.is_ascii_alphabetic())\n        && chars.all(|ch| ch == '_' || ch.is_ascii_alphanumeric())\n}\n\nfn write_lane_environment(path: &Path, environment: &[(String, String)]) -> Result<()> {\n    for (key, _) in environment {\n        if !valid_environment_key(key) {\n            bail!(\"invalid lane environment key {key:?}\");\n        }\n    }\n    let encoded = serde_json::to_vec(environment).context(\"serialize private lane environment\")?;\n    if encoded.len() as u64 > MAX_ENVIRONMENT_BYTES {\n        bail!(\n            \"private lane environment exceeds {} bytes\",\n            MAX_ENVIRONMENT_BYTES\n        );\n    }\n\n    let tmp_path = lane_environment_tmp_path(path);\n    remove_file_if_present(path)?;\n    remove_file_if_present(&tmp_path)?;\n    let mut options = OpenOptions::new();\n    options.create_new(true).write(true);\n    #[cfg(unix)]\n    {\n        use std::os::unix::fs::OpenOptionsExt;","sourceCodeStart":216,"sourceCodeEnd":252,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/0c42157ee52f9d55af2b506d71b46249910f77d3/crates/lane/src/runtime.rs#L216-L252","documentation":"Thrown by write_lane_environment when an environment key fails the identifier check: the first character must be '_' or an ASCII letter, and the rest must be '_' or ASCII alphanumeric ([A-Za-z_][A-Za-z0-9_]*). Values are unrestricted — only key names are validated. This matches what a shell can legally export, since the pairs are written as a private lane environment file.","triggerScenarios":"A LaneStartSpec.environment containing keys like \"my-var\" (hyphen), \"123\" (leading digit), \"foo.bar\" (dot), an empty string, or a non-ASCII/unicode key. Collecting the ambient environment of a tool that permits hyphenated names and passing it through unchanged triggers this on the first bad key.","commonSituations":"Forwarding a parent process env dump that includes keys other languages allow; machine-generated keys from config systems (dot-notation like server.port); placeholders like \"-\" or \"\" sneaking in from templating.","solutions":["Rename keys to identifier form: my-var -> MY_VAR, foo.bar -> FOO_BAR","Filter/translate the environment at the boundary before building LaneStartSpec (map '-' and '.' to '_')","Drop empty-string keys — they can never be valid"],"exampleFix":"// before\nlet spec = LaneStartSpec { environment: vec![(\"my-var\".into(), \"1\".into())], .. };\n\n// after\nlet spec = LaneStartSpec { environment: vec![(\"MY_VAR\".into(), \"1\".into())], .. };","handlingStrategy":"type-guard","validationCode":"fn valid_env_key(key: &str) -> bool {\n    let mut chars = key.chars();\n    chars.next().is_some_and(|c| c == '_' || c.is_ascii_alphabetic())\n        && chars.all(|c| c == '_' || c.is_ascii_alphanumeric())\n}\n\nlet environment: Vec<(String, String)> = environment\n    .into_iter()\n    .filter(|(k, _)| valid_env_key(k))\n    .collect();","typeGuard":"fn is_valid_lane_env_key(key: &str) -> bool {\n    let mut chars = key.chars();\n    chars.next().is_some_and(|c| c == '_' || c.is_ascii_alphabetic())\n        && chars.all(|c| c == '_' || c.is_ascii_alphanumeric())\n}","tryCatchPattern":null,"preventionTips":["Sanitize keys at the boundary: map '-' and '.' to '_', drop empties and leading digits","Forward only an allowlist of variable names instead of the whole ambient environment"],"tags":["rust","lane","environment-variables","validation"],"backgroundTag":"invalid-environment-variable-name","analyzedSha":"0c42157ee52f9d55af2b506d71b46249910f77d3","analyzedAt":"2026-08-20T21:50:45.477Z","schemaVersion":2},"datasetVersion":"2026-08-21T18:17:14.833Z"}