{"record":{"id":"6ae8a2e636a09368","repo":"Hmbown/CodeWhale","slug":"private-lane-environment-exceeds-bytes","errorCode":null,"errorMessage":"private lane environment exceeds {} bytes","messagePattern":"private lane environment exceeds (.+?) bytes","errorType":"validation","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/lane/src/runtime.rs","lineNumber":239,"sourceCode":"}\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;\n        options.mode(0o600);\n    }\n    let result = (|| {\n        let mut file = options\n            .open(&tmp_path)","sourceCodeStart":221,"sourceCodeEnd":257,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/0c42157ee52f9d55af2b506d71b46249910f77d3/crates/lane/src/runtime.rs#L221-L257","documentation":"Thrown by write_lane_environment when the JSON serialization of the environment pairs exceeds MAX_ENVIRONMENT_BYTES, a fixed 1 MiB (1024*1024 bytes) cap. The size is measured on the serialized Vec of (String, String) pairs, so both key and value text counts. The check runs before any file is written, so no partial state lands on disk.","triggerScenarios":"Passing large values in LaneStartSpec.environment: base64 certificates/keys, embedded JIT bundles, dumped tokens, or wholesale forwarding of a huge parent environment. Serializing tips the total past 1 MiB and the write bails.","commonSituations":"Trying to smuggle a cert or credentials blob through env vars because it was convenient; CI wrappers forwarding entire build environments; generated environments with multi-hundred-KB values.","solutions":["Trim the environment to only the variables the lane actually needs","Move large blobs (certs, key material) to a file inside the worktree/workspace and pass its path via a small env var","If forwarding ambient env, filter to an allowlist of variable names instead of passing everything"],"exampleFix":"// before\nenvironment: vec![(\"CA_BUNDLE\".into(), std::fs::read_to_string(\"ca.pem\")?)], // ~2 MB inline\n\n// after\nenvironment: vec![(\"CA_BUNDLE_PATH\".into(), \"/workspace/ca.pem\".into())],","handlingStrategy":"validation","validationCode":"const MAX_ENVIRONMENT_BYTES: usize = 1024 * 1024;\n\nlet encoded = serde_json::to_vec(&environment)?;\nanyhow::ensure!(\n    encoded.len() as u64 <= MAX_ENVIRONMENT_BYTES as u64,\n    \"lane environment serializes to {} bytes; trim it or pass file paths instead of blobs\",\n    encoded.len()\n);","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Pass paths to large artifacts (certs, bundles) instead of inlining their contents in env vars","Measure the serialized size of generated environments before submitting the lane spec","Use an allowlist when forwarding ambient environment to lanes"],"tags":["rust","lane","environment-variables","payload-size","validation"],"backgroundTag":"payload-too-large","analyzedSha":"0c42157ee52f9d55af2b506d71b46249910f77d3","analyzedAt":"2026-08-20T21:50:45.477Z","schemaVersion":2},"datasetVersion":"2026-08-21T18:17:14.833Z"}