BigPizzaV3/CodexPlusPlus · error · anyhow::Error

Dream Skin theme backup belongs to a different config.toml

Error message

Dream Skin theme backup belongs to a different config.toml

What it means

restore_base_theme reads the JSON backup and compares its stored config_path against the current config.toml path (textual comparison via to_string_lossy). A mismatch means the backup was captured against a different config.toml; restoring would corrupt this one, so it refuses. When no backup file exists, restore is a clean no-op.

Source

Thrown at crates/codex-plus-core/src/dream_skin.rs:300

    }
    if uses_native_chrome_theme(theme) {
        restore_backup_setting(desktop, &backup, "appearanceLightCodeThemeId")?;
        restore_backup_setting(desktop, &backup, "appearanceLightChromeTheme")?;
    } else {
        desktop["appearanceLightCodeThemeId"] = value("codex");
        desktop["appearanceLightChromeTheme"] =
            Item::Value(Value::InlineTable(target_chrome_theme(theme, profile)));
    }
    write_config(config_path, document.to_string().as_bytes())
}

fn restore_base_theme(config_path: &Path, backup_path: &Path) -> anyhow::Result<()> {
    if !backup_path.exists() {
        return Ok(());
    }
    let backup = read_backup(backup_path)?;
    if backup.config_path != config_path.to_string_lossy() {
        bail!("Dream Skin theme backup belongs to a different config.toml");
    }

    let existing = read_config_or_empty(config_path)?;
    let mut document = parse_config(&existing, config_path)?;
    if document.get("desktop").is_none() {
        document["desktop"] = Item::Table(Table::new());
    }
    let desktop = desktop_table_mut(&mut document)?;
    for key in APPEARANCE_KEYS {
        match backup.values.get(key).and_then(Option::as_deref) {
            Some(serialized) => {
                desktop[key] = parse_backup_item(&backup, serialized)
                    .with_context(|| format!("failed to restore Dream Skin setting {key}"))?;
            }
            None => {
                desktop.remove(key);
            }
        }

View on GitHub (pinned to f2074595a2)

Solutions

  1. Restore while pointed at the same config.toml path that was active when the theme was applied
  2. If the old path is gone for good, delete the stale backup file - restore then no-ops cleanly
  3. Set CODEX_HOME consistently across apply and restore
Defensive patterns

Strategy: validation

Validate before calling

fn backup_targets_config(backup_path: &std::path::Path, config_path: &std::path::Path) -> bool {
    std::fs::read(backup_path)
        .ok()
        .and_then(|b| serde_json::from_slice::<serde_json::Value>(&b).ok())
        .and_then(|v| {
            v.get("config_path")?
                .as_str()
                .map(|s| s == config_path.to_string_lossy())
        })
        .unwrap_or(false)
}

Prevention

When it happens

Trigger: CODEX_HOME changed, or config.toml was moved or renamed, between applying a theme and restoring it; a state dir reused with a different config path layout.

Common situations: Users relocating their codex home; machine migration; one state dir shared by several profiles.

Related errors


AI-assisted analysis of BigPizzaV3/CodexPlusPlus@f2074595a2 (2026-08-23). Data as JSON: /api/errors/745adf5be2d4c221. Report an issue: GitHub.