Hmbown/CodeWhale · warning

tempdir

Error message

tempdir

What it means

tempfile::tempdir().expect("tempdir") in config_store_save_preserves_comments (crates/config/src/tests.rs:4215) panics if a temporary directory cannot be created. Similar tempdir expects appear across the codebase (e.g. crates/agent/src/lib.rs:2237). tempfile fails when it cannot find a writable temp location or lacks permission.

Solutions

  1. Ensure TMPDIR is set to an existing writable directory (or unset it to fall back to /tmp).
  2. Check disk space and mount flags on the temp filesystem.
  3. In restricted environments, set tempfile's env override to a directory inside the workspace before running cargo test.

Example fix

// before
cargo test -p codewhale-config   # TMPDIR=/nonexistent
// after
export TMPDIR=$(mktemp -d)
cargo test -p codewhale-config
Defensive patterns

Strategy: fallback

Validate before calling

let tmp = std::env::var("TMPDIR").unwrap_or_else(|_| "/tmp".into());
assert!(std::path::Path::new(&tmp).is_dir(), "TMPDIR {tmp} is not a directory");

Try / catch

let dir = tempfile::tempdir()
    .unwrap_or_else(|e| panic!("tempdir unavailable ({e}); check TMPDIR and /tmp permissions"));

Prevention

When it happens

Trigger: TMPDIR points to a nonexistent or read-only directory; TMPDIR unset with /tmp unwritable (sandboxed/locked-down CI runner); disk full.

Common situations: Containerized CI with read-only /tmp; setting TMPDIR to a path that does not exist; running tests as a restricted user.

Understand the failure class

Background: "environment variable is not set" and "Missing keys in environment" errors: what missing required env var messages mean and how to fix them — this error's family across 28 libraries.

Related errors


AI-assisted analysis of Hmbown/CodeWhale@433685b202 (2026-09-15). Data as JSON: /api/errors/9fbddb4bf48b3f5c. Report an issue: GitHub.

Appendix: source

Thrown at crates/config/src/tests.rs:4215

    fs::write(&path, "model = \"new-model\"\n").expect("seed config");
    let backup_path = config_backup_path(&path);
    fs::write(
        &backup_path,
        "api_key = \"old-test-credential\"\nmodel = \"old-model\"\n",
    )
    .expect("seed backup");

    scrub_plaintext_api_keys_from_config_backup(&path).expect("scrub backup");

    let backup = fs::read_to_string(backup_path).expect("read backup");
    assert!(!backup.contains("old-test-credential"), "{backup}");
    assert!(!backup.contains("api_key"), "{backup}");
    assert!(backup.contains("model = \"old-model\""));
}

#[test]
fn config_store_save_preserves_comments() {
    let dir = tempfile::tempdir().expect("tempdir");
    let config_path = dir.path().join(CONFIG_FILE_NAME);
    let original = "# my model\nmodel = \"deepseek-v4-flash\"\n# end comment\n";
    fs::write(&config_path, original).expect("write config");

    let mut store = ConfigStore::load(Some(config_path.clone())).expect("load config store");
    store.config.model = Some("deepseek-v4-pro".to_string());
    store.save().expect("save");

    let body = fs::read_to_string(&config_path).expect("read config");
    assert!(body.contains("# my model"), "prefix comment preserved");
    assert!(body.contains("# end comment"), "suffix comment preserved");
    assert!(body.contains("model = \"deepseek-v4-pro\""));
}

#[test]
fn config_store_save_preserves_disabled_keys() {
    let dir = tempfile::tempdir().expect("tempdir");
    let config_path = dir.path().join(CONFIG_FILE_NAME);

View on GitHub (pinned to 433685b202)