BigPizzaV3/CodexPlusPlus · error

Grok 配置已被其他程序修改,请刷新后再保存

Error message

Grok 配置已被其他程序修改,请刷新后再保存

What it means

save_grok_config_at implements optimistic concurrency for the Grok config.toml: it recomputes a revision (hash) from the current file bytes and requires it to match request.revision before writing. If another process modified the file since the caller read it, the revisions differ and the library bails with this error instead of clobbering the external changes.

Solutions

  1. Re-read the current Grok config via the read API to obtain a fresh revision.
  2. Re-apply your edits on top of the refreshed config and save with the new revision.
  3. Identify the other process/editor modifying config.toml and avoid concurrent edits.
  4. Retry the save immediately after refresh if no external writer exists (stale cached revision in the UI).
  5. Do not force-overwrite; the check exists to prevent losing external changes.

Example fix

// before: save with stale revision
save_grok_config(SaveGrokConfigRequest { revision: old_revision, .. })?;
// after: refresh then save
let current = read_grok_config()?;
save_grok_config(SaveGrokConfigRequest { revision: current.revision, .. })?;
Defensive patterns

Strategy: retry

Validate before calling

let current = read_grok_config()?;
if current.revision != request_revision {
    return Err("stale revision: refresh config before saving".into());
}

Type guard

fn revision_is_fresh(rev: &str, current: &GrokConfig) -> bool {
    rev == current.revision
}

Try / catch

loop {
    match save_grok_config(req_with_fresh_revision()).await {
        Err(e) if e.to_string().contains("已被其他程序修改") => continue, // refresh & retry
        other => break other,
    }
}

Prevention

When it happens

Trigger: Calling save_grok_config (or save_grok_config_at) with a SaveGrokConfigRequest whose revision field no longer equals revision_for(&current config.toml bytes) — i.e. the file changed between the caller's read and the save.

Common situations: Two manager windows or two processes editing Grok config simultaneously; an external tool (CLI, editor) rewrote config.toml after the UI loaded it; long-lived UI session holding a stale revision while a background process saved.

Understand the failure class

Background: Conflicting config options: "cannot be used together" — configuration validation errors across open-source libraries — this error's family across 162 libraries.

Related errors


AI-assisted analysis of BigPizzaV3/CodexPlusPlus@b1ed92e5e4 (2026-09-19). Data as JSON: /api/errors/34ca9f352948952d. Report an issue: GitHub.

Appendix: source

Thrown at crates/codex-plus-core/src/grok_config.rs:139

pub fn save_grok_config(
    request: &SaveGrokConfigRequest,
    backup_root: &Path,
) -> anyhow::Result<SaveGrokConfigResult> {
    save_grok_config_at(&default_grok_home_dir(), request, backup_root)
}

pub fn save_grok_config_at(
    home: &Path,
    request: &SaveGrokConfigRequest,
    backup_root: &Path,
) -> anyhow::Result<SaveGrokConfigResult> {
    validate_request(request)?;

    let config_path = home.join(CONFIG_FILE);
    let previous = read_optional_bytes(&config_path)?;
    let actual_revision = revision_for(&previous);
    if request.revision != actual_revision {
        bail!("Grok 配置已被其他程序修改,请刷新后再保存");
    }
    let text = std::str::from_utf8(&previous).context("Grok config.toml 不是有效的 UTF-8")?;
    let mut doc = parse_document(text, &config_path)?;

    update_root_string(&mut doc, "models", "default", &request.default_model)?;
    update_root_string(
        &mut doc,
        "endpoints",
        "models_base_url",
        &request.models_base_url,
    )?;
    update_models(&mut doc, &request.models)?;

    let mut updated = doc.to_string();
    if !updated.is_empty() && !updated.ends_with('\n') {
        updated.push('\n');
    }
    let backup_path = create_backup(&config_path, &previous, backup_root)?;

View on GitHub (pinned to b1ed92e5e4)