Hmbown/CodeWhale · warning

seed backup

Error message

seed backup

What it means

A test assertion helper: fs::write(&path, ...).expect("seed backup") panics if writing the seed backup file fails in the scrub_plaintext_api_keys_from_config_backup test at crates/config/src/tests.rs:4203. It is not a library error — it indicates the test fixture could not create its backup file.

Solutions

  1. Ensure each test creates its own tempfile::tempdir() instead of sharing a path across tests.
  2. Check the io::Error in the panic message: NotFound -> missing dir, PermissionDenied -> fs perms, IsADirectory -> wrong path.
  3. Run the single test in isolation (cargo test -p codewhale-config <name>) to rule out parallel-test interference.
Defensive patterns

Strategy: validation

Validate before calling

assert!(backup_path.parent().is_some());
std::fs::create_dir_all(backup_path.parent().unwrap())?;

Try / catch

if let Err(e) = fs::write(&backup_path, contents) {
    panic!("seed backup failed ({e:?}): is the tempdir still alive?");
}

Prevention

When it happens

Trigger: The temp directory was deleted before the write, the path is a directory, the disk is full, or permission denied on the fixture path.

Common situations: Flaky CI with parallel tests clobbering a shared path; tests refactored to share one tempdir where one test removed it.

Understand the failure class

Background: "failed to write file", "Could not save figure", "Error saving remote file" — file write failed: causes and fixes across languages and libraries — this error's family across 38 libraries.

Related errors


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

Appendix: source

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

            .lines()
            .any(|line| line.trim_start().starts_with("api_key ="))
    );
    assert!(backup.contains("api_key_env = \"OPENROUTER_API_KEY\""));
    assert!(backup.contains("auth_mode = \"api_key\""));
    assert!(backup.contains("default_text_model = \"deepseek-v4-pro\""));
}

#[test]
fn config_backup_scrub_repairs_an_existing_plaintext_backup() {
    let dir = tempfile::tempdir().expect("tempdir");
    let path = dir.path().join(CONFIG_FILE_NAME);
    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());

View on GitHub (pinned to 433685b202)