{"record":{"id":"509f6f3d654e2279","repo":"Hmbown/CodeWhale","slug":"rendered-body","errorCode":null,"errorMessage":"rendered body","messagePattern":"rendered body","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/config/src/tests.rs","lineNumber":4298,"sourceCode":"    // #3410: the comment-preserving ConfigStore write must compose with\n    // SetupTransaction so a setup step can update config.toml atomically\n    // alongside sibling setup files.\n    let dir = tempfile::tempdir().expect(\"tempdir\");\n    let config_path = dir.path().join(CONFIG_FILE_NAME);\n    let state_path = dir.path().join(crate::setup_state::SETUP_STATE_FILE_NAME);\n    fs::write(\n        &config_path,\n        \"# my model\\nmodel = \\\"deepseek-v4-flash\\\"\\n# end comment\\n\",\n    )\n    .expect(\"write config\");\n\n    let mut store = ConfigStore::load(Some(config_path.clone())).expect(\"load config store\");\n    store.config.model = Some(\"deepseek-v4-pro\".to_string());\n\n    let mut transaction = persistence::SetupTransaction::new();\n    transaction.stage(\n        &config_path,\n        store.rendered_body().expect(\"rendered body\").into_bytes(),\n    );\n    transaction\n        .stage_json(&state_path, &SetupState::default())\n        .expect(\"stage setup state\");\n    transaction.commit().expect(\"commit\");\n\n    let body = fs::read_to_string(&config_path).expect(\"read config\");\n    assert!(body.contains(\"# my model\"), \"prefix comment preserved\");\n    assert!(body.contains(\"# end comment\"), \"suffix comment preserved\");\n    assert!(body.contains(\"model = \\\"deepseek-v4-pro\\\"\"));\n    assert!(state_path.exists(), \"sibling setup state written\");\n}\n\n#[test]\nfn setup_transaction_rolls_back_config_store_body_on_sibling_failure() {\n    // #3410 rollback expectation: when a sibling stage fails to apply, the\n    // already-written config.toml is restored byte-for-byte, comments and\n    // all — no half-applied setup.","sourceCodeStart":4280,"sourceCodeEnd":4316,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/433685b2024e7bc4c99e1e2e326bcad39b4d9d65/crates/config/src/tests.rs#L4280-L4316","documentation":"This panic comes from `store.rendered_body().expect(\"rendered body\")` at crates/config/src/tests.rs:4298. `rendered_body()` (crates/config/src/lib.rs:5569) validates configured models, serializes the config to pretty TOML, fixes the provider field, and merges comments back from the originally loaded raw text. It fails when validation rejects a value, TOML serialization fails, or `merge_and_preserve_comments` decides the original snapshot is unmergeable and cannot safely preserve it.","triggerScenarios":"Calling `rendered_body()` on a store whose config contains entries failing `validate_configured_models`, whose serialization cannot be parsed back by toml_edit, or whose `original_raw` no longer structurally matches the serialized output.","commonSituations":"Custom models violating catalog validation rules; a loaded file that was mutated on disk after load; exotic TOML constructs that the comment-merge cannot reconcile with a fresh serialization.","solutions":["Fix the config value that fails validate_configured_models before rendering","Reload the store so `original_raw` matches the current on-disk file","If the merge is fundamentally unmergeable, fall back to a plain serialized body and accept comment loss"],"exampleFix":"// before\ntransaction.stage(&config_path, store.rendered_body().expect(\"rendered body\").into_bytes());\n// after\nlet body = store.rendered_body()\n    .map_err(|e| panic!(\"rendered body: {e:#}\"))\n    .unwrap();\ntransaction.stage(&config_path, body.into_bytes());","handlingStrategy":"validation","validationCode":"// validate configured models before rendering\ncodewhale_config::catalog::configured::validate_configured_models(\n    store.config().custom_models.as_deref().unwrap_or_default(),\n).map_err(|e| format!(\"invalid custom models: {e}\"))?;","typeGuard":null,"tryCatchPattern":"match store.rendered_body() {\n    Ok(body) => transaction.stage(&config_path, body.into_bytes()),\n    Err(e) => return Err(anyhow!(\"cannot render config body: {e:#}\")),\n}","preventionTips":["Fix invalid custom_models entries before saving","Reload the store so original_raw matches disk before rendering","Accept comment loss as a fallback when the merge is unmergeable"],"tags":["rust","toml","serialization","comment-preservation"],"backgroundTag":"json-serialization-failed","analyzedSha":"433685b2024e7bc4c99e1e2e326bcad39b4d9d65","analyzedAt":"2026-09-15T12:24:24.634Z","contentChangedAt":"2026-09-15T12:24:24.634Z","schemaVersion":2},"datasetVersion":"2026-09-23T02:17:17.105Z"}