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
- Re-read the current Grok config via the read API to obtain a fresh revision.
- Re-apply your edits on top of the refreshed config and save with the new revision.
- Identify the other process/editor modifying config.toml and avoid concurrent edits.
- Retry the save immediately after refresh if no external writer exists (stale cached revision in the UI).
- 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
- Always re-read the config immediately before saving to get a fresh revision.
- Avoid two simultaneous editors/processes writing the same config.toml.
- Apply edits to the freshly-read document rather than a long-lived cached copy.
- Treat this error as 'reload and re-apply', never force-overwrite.
When it happens
Trigger: Calling save_grok_config (or save_grok_config_at) with a SaveGrokConfigRequest whose revision field no longer equals revision_for(¤t 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
- 当前 Codex 配置使用外部 model_catalog_json,无法同时应用每模型窗口、自动压缩或元数据
- Grok 模型别名与未管理配置冲突:
- Grok 模型别名不能为空
- 当前配置使用外部 model_catalog_json,无法同时应用每模型窗口、自动压缩或元数据
- restore conflict: agent_job_items row already assigned
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)