Hmbown/CodeWhale · error

stage setup state

Error message

stage setup state

What it means

This panic comes from `.expect("stage setup state")` on `SetupTransaction::stage_json(&state_path, &SetupState::default())` at crates/config/src/tests.rs:4302. `stage_json` serializes the value to JSON and stages the bytes for atomic application at commit. It fails when `SetupState` cannot be serialized to JSON or the staged path is invalid/unwritable by transaction rules.

Solutions

  1. Check that the state path's parent is a real directory
  2. Verify SetupState derives Serialize and every field is JSON-serializable
  3. Confirm the tempdir is still alive at staging time

Example fix

// before
transaction.stage_json(&state_path, &SetupState::default()).expect("stage setup state");
// after
assert!(state_path.parent().map_or(false, |p| p.is_dir()), "state parent not a directory");
transaction.stage_json(&state_path, &SetupState::default())
    .map_err(|e| panic!("stage setup state: {e:#}"))
    .unwrap();
Defensive patterns

Strategy: validation

Validate before calling

assert!(state_path.parent().map_or(false, |p| p.is_dir()), "state parent must be a directory");
serde_json::to_vec(&SetupState::default()).map_err(|e| format!("SetupState not serializable: {e}"))?;

Prevention

When it happens

Trigger: Staging a `SetupState` value that fails serde JSON serialization, or a `state_path` whose parent directory does not exist or is not a directory (in this test's sibling test, a regular file 'blocker' occupies the parent slot to force failure).

Common situations: A serde rename/skip misconfiguration making a field unserializable; pointing stage_json at a path under a file rather than a directory; a stale path from a removed tempdir.

Understand the failure class

Background: "JSON serialization failed", "not JSON serializable", "Failed to serialize": why JSON marshaling errors happen and how to fix them — this error's family across 46 libraries.

Related errors


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

Appendix: source

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

    let config_path = dir.path().join(CONFIG_FILE_NAME);
    let state_path = dir.path().join(crate::setup_state::SETUP_STATE_FILE_NAME);
    fs::write(
        &config_path,
        "# my model\nmodel = \"deepseek-v4-flash\"\n# end comment\n",
    )
    .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());

    let mut transaction = persistence::SetupTransaction::new();
    transaction.stage(
        &config_path,
        store.rendered_body().expect("rendered body").into_bytes(),
    );
    transaction
        .stage_json(&state_path, &SetupState::default())
        .expect("stage setup state");
    transaction.commit().expect("commit");

    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\""));
    assert!(state_path.exists(), "sibling setup state written");
}

#[test]
fn setup_transaction_rolls_back_config_store_body_on_sibling_failure() {
    // #3410 rollback expectation: when a sibling stage fails to apply, the
    // already-written config.toml is restored byte-for-byte, comments and
    // all — no half-applied setup.
    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");

View on GitHub (pinned to 433685b202)