{"record":{"id":"34ca9f352948952d","repo":"BigPizzaV3/CodexPlusPlus","slug":"grok","errorCode":null,"errorMessage":"Grok 配置已被其他程序修改，请刷新后再保存","messagePattern":"Grok 配置已被其他程序修改，请刷新后再保存","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/codex-plus-core/src/grok_config.rs","lineNumber":139,"sourceCode":"pub fn save_grok_config(\n    request: &SaveGrokConfigRequest,\n    backup_root: &Path,\n) -> anyhow::Result<SaveGrokConfigResult> {\n    save_grok_config_at(&default_grok_home_dir(), request, backup_root)\n}\n\npub fn save_grok_config_at(\n    home: &Path,\n    request: &SaveGrokConfigRequest,\n    backup_root: &Path,\n) -> anyhow::Result<SaveGrokConfigResult> {\n    validate_request(request)?;\n\n    let config_path = home.join(CONFIG_FILE);\n    let previous = read_optional_bytes(&config_path)?;\n    let actual_revision = revision_for(&previous);\n    if request.revision != actual_revision {\n        bail!(\"Grok 配置已被其他程序修改，请刷新后再保存\");\n    }\n    let text = std::str::from_utf8(&previous).context(\"Grok config.toml 不是有效的 UTF-8\")?;\n    let mut doc = parse_document(text, &config_path)?;\n\n    update_root_string(&mut doc, \"models\", \"default\", &request.default_model)?;\n    update_root_string(\n        &mut doc,\n        \"endpoints\",\n        \"models_base_url\",\n        &request.models_base_url,\n    )?;\n    update_models(&mut doc, &request.models)?;\n\n    let mut updated = doc.to_string();\n    if !updated.is_empty() && !updated.ends_with('\\n') {\n        updated.push('\\n');\n    }\n    let backup_path = create_backup(&config_path, &previous, backup_root)?;","sourceCodeStart":121,"sourceCodeEnd":157,"githubUrl":"https://github.com/BigPizzaV3/CodexPlusPlus/blob/b1ed92e5e4a2d74095d4b8db5af43cef7acba9c6/crates/codex-plus-core/src/grok_config.rs#L121-L157","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before: save with stale revision\nsave_grok_config(SaveGrokConfigRequest { revision: old_revision, .. })?;\n// after: refresh then save\nlet current = read_grok_config()?;\nsave_grok_config(SaveGrokConfigRequest { revision: current.revision, .. })?;","handlingStrategy":"retry","validationCode":"let current = read_grok_config()?;\nif current.revision != request_revision {\n    return Err(\"stale revision: refresh config before saving\".into());\n}","typeGuard":"fn revision_is_fresh(rev: &str, current: &GrokConfig) -> bool {\n    rev == current.revision\n}","tryCatchPattern":"loop {\n    match save_grok_config(req_with_fresh_revision()).await {\n        Err(e) if e.to_string().contains(\"已被其他程序修改\") => continue, // refresh & retry\n        other => break other,\n    }\n}","preventionTips":["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."],"tags":["grok-config","optimistic-concurrency","conflict"],"backgroundTag":"conflicting-config-options","analyzedSha":"b1ed92e5e4a2d74095d4b8db5af43cef7acba9c6","analyzedAt":"2026-09-19T23:35:21.129Z","contentChangedAt":"2026-09-19T23:35:21.129Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}