{"record":{"id":"091e458e469c764d","repo":"janhq/jan","slug":"failed-to-serialize-mcp-settings","errorCode":null,"errorMessage":"Failed to serialize MCP settings","messagePattern":"Failed to serialize MCP settings","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"src-tauri/src/core/mcp/commands.rs","lineNumber":801,"sourceCode":") -> Result<(), String> {\n    let mut path = get_jan_data_folder_path(app.clone());\n    path.push(\"mcp_config.json\");\n    log::info!(\"save mcp configs, path: {path:?}\");\n\n    let mut config_value: Value =\n        serde_json::from_str(&configs).map_err(|e| format!(\"Invalid MCP config payload: {e}\"))?;\n\n    if !config_value.is_object() {\n        return Err(\"MCP config must be a JSON object\".to_string());\n    }\n\n    let config_object = config_value.as_object_mut().unwrap();\n    let settings = parse_mcp_settings(config_object.get(\"mcpSettings\"));\n\n    if !config_object.contains_key(\"mcpSettings\") {\n        config_object.insert(\n            \"mcpSettings\".to_string(),\n            serde_json::to_value(&settings).expect(\"Failed to serialize MCP settings\"),\n        );\n    }\n\n    if !config_object.contains_key(\"mcpServers\") {\n        config_object.insert(\"mcpServers\".to_string(), json!({}));\n    }\n\n    fs::write(\n        &path,\n        serde_json::to_string_pretty(&config_value)\n            .map_err(|e| format!(\"Failed to serialize MCP config: {e}\"))?,\n    )\n    .map_err(|e| e.to_string())?;\n\n    {\n        let state = app.state::<AppState>();\n        let mut settings_guard = state.mcp_settings.lock().await;\n        *settings_guard = settings;","sourceCodeStart":783,"sourceCodeEnd":819,"githubUrl":"https://github.com/janhq/jan/blob/fad3f12a147d138388a66f0d92a02b2675f65294/src-tauri/src/core/mcp/commands.rs#L783-L819","documentation":"This is a panic (.expect) from serde_json::to_value(&settings) when serializing the McpSettings struct to inject it into the config JSON during save_mcp_configs. Since settings was just parsed from the same JSON (via parse_mcp_settings), a serialization failure here indicates a structural problem in the McpSettings Serialize derive — a non-serializable field type, or a custom Serialize impl that errors. In practice this should never fire; if it does, it points to a code bug rather than user data.","triggerScenarios":"A new field added to McpSettings that does not implement Serialize. A custom serde attribute (e.g. #[serde(serialize_with=...)]) that panics on certain values. An interior enum variant that serde cannot represent as a map value. PhantomData or non-serializable marker types in the struct.","commonSituations":"Plugin version mismatch where the struct changed but the binary is old. Manual edits to the McpSettings struct introducing a non-serializable type. A serde rename/tag conflict causing recursive serialization failure.","solutions":["Inspect the McpSettings struct definition for any field lacking a Serialize impl.","Check for custom serde attributes that may fail on specific enum variants.","Add a unit test that round-trips McpSettings through serde_json to catch regressions.","Replace .expect with a proper error return to avoid crashing the app on this path."],"exampleFix":"// before\nserde_json::to_value(&settings).expect(\"Failed to serialize MCP settings\")\n\n// after\nserde_json::to_value(&settings)\n    .map_err(|e| format!(\"Failed to serialize MCP settings: {e}\"))?","handlingStrategy":"try-catch","validationCode":"// Add a round-trip test to catch serialization regressions early\n#[test]\nfn mcp_settings_roundtrip() {\n    let settings = McpSettings::default();\n    let value = serde_json::to_value(&settings)\n        .expect(\"McpSettings must be serializable\");\n    let back: McpSettings = serde_json::from_value(value)\n        .expect(\"McpSettings must be deserializable from its own serialization\");\n    assert_eq!(settings, back);\n}","typeGuard":null,"tryCatchPattern":"// Replace .expect with a proper error\nlet settings_value = serde_json::to_value(&settings)\n    .map_err(|e| format!(\"Failed to serialize MCP settings: {e}\"))?;\nconfig_object.insert(\"mcpSettings\".to_string(), settings_value);","preventionTips":["Run a round-trip serialization test for McpSettings in CI.","Never add non-Serialize fields to McpSettings without testing the config save path.","Use .map_err instead of .expect for any serialization in a request-handling code path.","Add #[derive(Serialize, Deserialize)] tests for all config structs."],"tags":["panic","serde","serialization","mcp","config"],"backgroundTag":null,"analyzedSha":"fad3f12a147d138388a66f0d92a02b2675f65294","analyzedAt":"2026-08-12T20:33:47.516Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}