siyuan-note/siyuan · error
read agent runtime failed: %w
Error message
read agent runtime failed: %w
What it means
When a save carries a commitTurnID (or recoveryTurnID), SaveSessionState loads the per-session agent runtime state to verify and apply the finished turn (kernel/agent/session.go:384-387). If loadRuntimeLocked fails — the runtime state file is unreadable or corrupt — the commit is refused with 'read agent runtime failed' because committing without the runtime's authoritative turn content would lose data.
Source
Thrown at kernel/agent/session.go:387
if _, ok := newData[k]; !ok {
// messages 是已废弃的旧会话字段,不再带入新格式;其他未知字段原样保留,
// 避免前后端版本不一致时擦除较新版本写入的数据。
if k != "messages" && k != "expectedRevision" && k != "commitTurnID" &&
k != "recoveryTurnID" && k != "recoveryState" && k != "recoveryRevision" && k != "agentRunning" {
newData[k] = v
}
}
}
}
} else if err != nil && !os.IsNotExist(err) {
return 0, nil, fmt.Errorf("read session file failed: %w", err)
} else if meta.ExpectedRevision != nil && *meta.ExpectedRevision != 0 {
return 0, nil, ErrSessionConflict
}
if commitTurnID != "" {
runtime, err := loadRuntimeLocked(meta.ID)
if err != nil {
return currentRevision, nil, fmt.Errorf("read agent runtime failed: %w", err)
}
if runtime.ActiveTurn != nil {
if runtime.ActiveTurn.TurnID != commitTurnID {
return currentRevision, nil, ErrSessionConflict
}
if !isRuntimeTurnTerminal(runtime.ActiveTurn) {
return currentRevision, nil, ErrRuntimeNotFinalized
}
if err := applyRuntimeTurnToSessionLocked(newData, runtime.ActiveTurn); err != nil {
return currentRevision, nil, err
}
} else if currentCommittedTurnID != commitTurnID {
return currentRevision, nil, ErrSessionConflict
}
}
newRevision := currentRevision + 1
newData["revision"] = newRevisionView on GitHub (pinned to afa823b6b4)
Solutions
- GET /api/ai/agent/getSession first — its FinalizeOrphanedTurn pass (kernel/api/agent.go:441) can repair/finalize an orphaned runtime turn so the subsequent commit succeeds
- Inspect the session directory for a truncated runtime state file and restore it from backup
- If unrepairable, remove the session (removeSession) and start a new one — the turn's runtime content is the authoritative record and must not be hand-faked
- Check what crashed or interrupted the write (power, OOM-kill, sync tool) to avoid recurrence
Defensive patterns
Strategy: try-catch
Validate before calling
null
Type guard
null
Try / catch
catch (e) {
if (/read agent runtime failed/.test(e?.data?.msg ?? '')) {
// first try GET /api/ai/agent/getSession to trigger FinalizeOrphanedTurn repair,
// then retry the commit with the same commitTurnID exactly once
}
} Prevention
- Always GET the session after a kernel crash before committing turns
- Do not delete runtime state files manually to 'clean up' — they hold the authoritative turn content
When it happens
Trigger: POST /api/ai/agent/saveSession with commitTurnID while the session's runtime state file (alongside session.json in data/storage/ai/agent/sessions/<id>/) is corrupt, zero-length, or unreadable — typically after a crash between writing runtime state and session.json, or partial sync of the workspace.
Common situations: Kernel killed mid-turn so the runtime file was half-written; workspace synchronized externally and the runtime file conflicted; disk issues. Often accompanied by the same session recovering oddly on next getSession (which runs FinalizeOrphanedTurn).
Related errors
- agent runtime turn is not finalized
- decode existing session data failed: %w
- invalid session id
- agent session revision conflict
- invalid session id
AI-assisted analysis of siyuan-note/siyuan@afa823b6b4 (2026-08-18).
Data as JSON: /api/errors/bd4dcf633093b581.
Report an issue: GitHub.