[Bug] v3.12.1 Windows:快速重启时 provider_config.json.lock 残留锁 → Personal Provider Config 降级为内存空配置(粘性),自定义 provider 全部 Provider Registry 中不存在
Nobody has claimed this yet.
- Dominant language
- No language data
- Stars
- 22
- Forks
- 1
- PR merge metrics
- No merged PRs in 30d
Description
提交前确认 · Pre-submission checklist
- 我已搜索过现有 issue,确认这不是重复(相近症状 #560/#622/#623 根因均不同,见文末对比)
- 我已阅读 CONTRIBUTING.md
问题类别 · Category: 模型设置 / 切换 · Model config
Agent 框架: ZCode Agent(自研)
严重程度 · Severity: 影响体验 · Major(对依赖自定义 provider 的用户为阻塞:全部不可用,但账号层 GLM 仍可用)
复现频率 · Reproducibility: 偶现 · Sometimes(快速重启竞态触发;触发后到下次重启前必现)
摘要 · Summary
Windows 上快速重启 ZCode 桌面端(旧实例尚在退出、新实例即启动)时,新实例加载 Personal Provider Config 会因残留锁文件 provider_config.json.lock(mkdir 型锁,EEXIST)报 ZCODE_FILE_LOCK_TIMEOUT,随后降级为"内存空配置"且该降级是粘性的——直到下次干净重启前,该实例及其全部子会话进程中,个人层 provider(自定义 API 供应商 + builtin:* 槽位)全部不可解析。
环境影响 · Environment
- ZCode 3.12.1(build dfc10615),CLI zcode 0.16.5,node v24.19.0
- Windows 11 23H2(10.0.22631 x64)
- 数据目录经 junction 别名访问(
C:\Users\28763\.zcode→E:\ZCodeData\.zcode)
复现步骤 · Steps to reproduce
- 配置若干自定义 provider(provider_config.json 规则 + config.json provider 表),正常使用;
- 退出并立即重新启动 ZCode 桌面端(旧实例退出与新实例启动窗口重叠);
- 新实例启动日志(v2/logs,
[provider-config]标签)出现:Personal Provider Config 加载失败,已保留磁盘状态并以内存空配置降级 {"error":{"code":"ZCODE_FILE_LOCK_TIMEOUT","path":"...\\.zcode\\v2\\provider_config.json", "syscall":"mkdir","cause":{"errno":-4075,"code":"EEXIST","syscall":"mkdir", "path":"...\\.zcode\\v2\\provider_config.json.lock"}}} - 此后在本实例任何会话中选用自定义 provider 模型必失败;fork 引用
builtin:*模型同样失败。
实际结果 · Actual behavior
- 会话内切换自定义 provider 模型(DeepSeek)发消息必报:
Model creation failed→ causeModelProtocolError: Provider Registry 中不存在 Provider: 49e753da-…(codeprovider_not_found),turnPhase: model_creation,重试同样失败; - fork 报:
Provider Registry 中不存在 Model: builtin:bigmodel-coding-plan/GLM-5.3; - 同一进程内
account:bigmodel-individual-coding-plan/GLM-5.3(账号层)请求正常——账号层与个人层故障并存; - UI 模型选择器仍能列出全部自定义模型(选择器读规则层),但选中即失败。
期望结果 · Expected behavior
- 启动时个人配置加载失败不应静默降级为空注册表:应回退磁盘上次良好快照(文件本身完好),或显式提示"provider 配置加载失败";
- mkdir EEXIST 的陈旧锁应可自愈(锁带持有者/租约信息,启动时检测无活进程持有即接管;或超时抢占),且进程退出路径应保证释放(含崩溃兜底清理孤儿锁);
- 快速重启场景下,新实例加载个人配置前应等待/检测旧实例锁释放,或对启动加载做有限重试。
证据与定位 · Evidence
- 时间线(本机,v2/logs 为本地时间):
- 18:35:18–28 新实例启动(14 进程),与旧实例(pid 14692)退出窗口重叠;
- 18:36:21
[provider-config] Personal Provider Config 加载失败,已保留磁盘状态并以内存空配置降级(ZCODE_FILE_LOCK_TIMEOUT / EEXIST onprovider_config.json.lock); - 18:36:29 / 18:36:37 轮询重试同样失败,此后无恢复记录(降级粘性);
- 19:49–20:05 该实例派生会话共 5 次 model_creation 失败(2 个会话 + 1 个 fork 子进程受累);
- 同日更早(旧实例内存视图完好):同配置下自定义 provider 请求全程正常。
- 配置文件本身健康:两份 JSON 合法、相关 provider 条目 enabled、同日 16:20 前一直可用——排除配置错误;
- 全新独立 CLI 进程读同一批文件注册表正常,并以 personal 层 provider 完整跑通模型请求——排除文件内容/格式问题,锁定"启动锁竞态 → 空配置降级"这一路径。
与相近 issue 的区别 · Related issues
- #622(重启后自定义 provider 丢失):那是 config 重建未迁移
source:custom,配置被写坏;本例配置文件完好,是运行时注册表降级。 - #560(provider.notInRegistry):症状相邻,但根因是 switchModelConfig 收敛时序,非启动锁降级。
- #623(stale bundled catalog):reasoning-effort 校验问题;本例是整个 provider 不在注册表。
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start with the startup provider-config path that loads v2/provider_config.json and the lock handling for provider_config.json.lock; inspect the v2/logs entries showing ZCODE_FILE_LOCK_TIMEOUT during rapid Windows restarts. Reproduce the overlapping restart and verify that a transient lock failure does not leave an empty personal registry, and that custom and builtin providers resolve afterward.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- node.js
- Domain
- desktop
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 48/100