janhq/jan · critical

Failed to serialize MCP settings

Error message

Failed to serialize MCP settings

What it means

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.

Source

Thrown at src-tauri/src/core/mcp/commands.rs:801

) -> Result<(), String> {
    let mut path = get_jan_data_folder_path(app.clone());
    path.push("mcp_config.json");
    log::info!("save mcp configs, path: {path:?}");

    let mut config_value: Value =
        serde_json::from_str(&configs).map_err(|e| format!("Invalid MCP config payload: {e}"))?;

    if !config_value.is_object() {
        return Err("MCP config must be a JSON object".to_string());
    }

    let config_object = config_value.as_object_mut().unwrap();
    let settings = parse_mcp_settings(config_object.get("mcpSettings"));

    if !config_object.contains_key("mcpSettings") {
        config_object.insert(
            "mcpSettings".to_string(),
            serde_json::to_value(&settings).expect("Failed to serialize MCP settings"),
        );
    }

    if !config_object.contains_key("mcpServers") {
        config_object.insert("mcpServers".to_string(), json!({}));
    }

    fs::write(
        &path,
        serde_json::to_string_pretty(&config_value)
            .map_err(|e| format!("Failed to serialize MCP config: {e}"))?,
    )
    .map_err(|e| e.to_string())?;

    {
        let state = app.state::<AppState>();
        let mut settings_guard = state.mcp_settings.lock().await;
        *settings_guard = settings;

View on GitHub (pinned to fad3f12a14)

Solutions

  1. Inspect the McpSettings struct definition for any field lacking a Serialize impl.
  2. Check for custom serde attributes that may fail on specific enum variants.
  3. Add a unit test that round-trips McpSettings through serde_json to catch regressions.
  4. Replace .expect with a proper error return to avoid crashing the app on this path.

Example fix

// before
serde_json::to_value(&settings).expect("Failed to serialize MCP settings")

// after
serde_json::to_value(&settings)
    .map_err(|e| format!("Failed to serialize MCP settings: {e}"))?
Defensive patterns

Strategy: try-catch

Validate before calling

// Add a round-trip test to catch serialization regressions early
#[test]
fn mcp_settings_roundtrip() {
    let settings = McpSettings::default();
    let value = serde_json::to_value(&settings)
        .expect("McpSettings must be serializable");
    let back: McpSettings = serde_json::from_value(value)
        .expect("McpSettings must be deserializable from its own serialization");
    assert_eq!(settings, back);
}

Try / catch

// Replace .expect with a proper error
let settings_value = serde_json::to_value(&settings)
    .map_err(|e| format!("Failed to serialize MCP settings: {e}"))?;
config_object.insert("mcpSettings".to_string(), settings_value);

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of janhq/jan@fad3f12a14 (2026-08-12). Data as JSON: /api/errors/091e458e469c764d. Report an issue: GitHub.