zai-org / zai-org/feedback

[Bug] Resumed sessions reject all sends with restoreWarning (ZCODE_RUNTIME_MODEL_UNAVAILABLE) — workspace-key normalization arms the guard on every resume; Enter appears dead, messages silently dropped

Open
#223 1 comment 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

priority: P2
Dominant language
No language data
Stars
22
Forks
1
PR merge metrics
No merged PRs in 30d

Description

问题类别 · Category

对话 / Agent 交互 · Agent chat (session resume)

涉及框架 · Framework

ZCode Agent(自研)

严重程度 · Severity / 复现频率 · Reproducibility

阻塞使用 · Blocking / 必现 · Always

版本 · Version / 环境 · Environment

3.7.3 (Linux AppImage) / custom OpenAI-compatible provider configured with an API key

问题描述 · Description

After quitting and relaunching ZCode, pre-existing conversations in certain workspaces cannot send messages: typing works, pressing Enter produces no visible result. Starting a new conversation works fine. The message is admitted then cancelled server-side with status_reason='restoreWarning' (protocol error -32031, code ZCODE_RUNTIME_MODEL_UNAVAILABLE), and no error is surfaced in the UI.

Notably, sessions in a workspace whose path retains a trailing slash (e.g. /path/to/project/) fail on every resume, at any time after launch — even minutes after the provider registry has synced. Sessions in a workspace without a trailing slash (e.g. the default conversation workspace) always work. The failure is deterministic per workspace.

复现步骤 · Steps to reproduce
  1. Have a project workspace whose path is seen by the agent with a trailing slash (e.g. /path/to/project/).
  2. Create a conversation there, exchange messages, then quit ZCode completely.
  3. Relaunch ZCode and open that conversation. Type a message and press Enter — nothing visibly happens.
  4. Wait any amount of time (even after the model list is fully populated) and try again — still rejected.
  5. A conversation in a workspace without the trailing slash (e.g. the default conversation workspace) sends fine immediately.
期望表现 · Expected behavior

A resumed session should accept messages as soon as the app is usable, exactly like a new conversation.

实际表现 · Actual behavior

Sends are rejected with -32031 while the restoreWarning guard is armed; the input ledger records status='cancelled', status_reason='restoreWarning' and the message is silently dropped (never posted, no UI feedback), so the session appears permanently broken.

根因 · Root cause (verified against the 3.7.3 bundle and runtime logs)

The agent runtime keys its workspace model catalog by the workspace key from the provider-registry path, which retains the trailing slash (/path/to/project/), while sessions carry a normalized workspace key without it (/path/to/project). The model-availability check H5 (bootstrap.zcode_protocol) performs an exact-key lookup (workspaceModelCatalogs.get(workspaceKey)), so for such workspaces it always misses:

  • Every resume then arms restoreWarning (type ZCODE_RUNTIME_MODEL_UNAVAILABLE, message "历史任务使用的模型已不可用…") and uses the deferred model adapter.
  • While armed, session/send, sendText and compact throw -32031; the input is cancelled in the ledger and dropped.
  • The clearing paths fail for these sessions: the provider-registry-arrival handler runs before the resume arms the warning (so it never clears it), and the workspace/readState clear path is gated on the app's current model matching the incoming runtime model — which the deferred adapter cannot satisfy — so it returns early without clearing.
  • A secondary race affects all workspaces on cold start: resumes issued before the provider-registry sync completes arm the warning for the first seconds after launch.

Runtime evidence (sanitized): zcode_protocol.session_resume.deferred_model_adapter … reason: "runtime_model_unavailable" — observed on resumes that happened after the catalog was already populated, with the session workspace key and the registry workspace key differing only by the trailing slash.

建议修复 · Proposed fix

Primary: normalize workspace keys — a single canonical form for catalog keys, session workspace records, and lookups (or make H5/catalog lookups tolerant of trailing-slash differences). This alone fixes the deterministic per-workspace failure.

Defense in depth:

  • Include the already-resolved runtime model in session/resume (the host already sends it in workspace/readState), so the guard cannot arm on the cold-start race.
  • Surface the -32031 / restoreWarning rejection in the composer instead of silently cancelling the input.

Possibly related: #173.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start by locating H5 in bootstrap.zcode_protocol and trace workspace keys from the provider-registry catalog through session resume and workspace/readState. Verify how trailing-slash normalization affects the restoreWarning guard and its clearing paths. Done means resumed sessions in trailing-slash workspaces accept sends reliably, including during cold start.

Written by the indexing model from the issue text.

Assessment

Domain
backend-api-design
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
45/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.