{"record":{"id":"469c7213e873dfab","repo":"siyuan-note/siyuan","slug":"invalid-agent-runtime-turn-state","errorCode":null,"errorMessage":"invalid agent runtime turn state","messagePattern":"invalid agent runtime turn state","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"kernel/agent/runtime.go","lineNumber":219,"sourceCode":"\t\treturn nil, err\n\t}\n\tif runtime.SchemaVersion > 1 {\n\t\treturn nil, fmt.Errorf(\"unsupported agent runtime schema version: %d\", runtime.SchemaVersion)\n\t}\n\tif runtime.SessionID != \"\" && runtime.SessionID != sessionID {\n\t\treturn nil, fmt.Errorf(\"agent runtime session id mismatch\")\n\t}\n\tif runtime.Revision < 0 {\n\t\treturn nil, fmt.Errorf(\"invalid agent runtime revision\")\n\t}\n\tif runtime.ActiveTurn != nil {\n\t\tif runtime.ActiveTurn.TurnID == \"\" {\n\t\t\treturn nil, fmt.Errorf(\"invalid agent runtime turn id\")\n\t\t}\n\t\tswitch runtime.ActiveTurn.State {\n\t\tcase \"running\", \"finished\", \"interrupted\":\n\t\tdefault:\n\t\t\treturn nil, fmt.Errorf(\"invalid agent runtime turn state\")\n\t\t}\n\t}\n\tif runtime.SchemaVersion == 0 {\n\t\truntime.SchemaVersion = 1\n\t}\n\tif runtime.SessionID == \"\" {\n\t\truntime.SessionID = sessionID\n\t}\n\treturn &runtime, nil\n}\n\nfunc writeRuntimeLocked(sessionID string, runtime *agentRuntime) error {\n\tif runtime == nil {\n\t\treturn nil\n\t}\n\t// runtime 只能附着在已经存在的会话上，避免迟到的 checkpoint 复活已删除会话。\n\tif _, err := os.Stat(filepath.Join(sessionsDir(), sessionID, \"session.json\")); err != nil {\n\t\treturn err","sourceCodeStart":201,"sourceCodeEnd":237,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/9f775e8a12daef8255556097396f9b2739078892/kernel/agent/runtime.go#L201-L237","documentation":"loadRuntimeLocked validates the ActiveTurn.State against the fixed set \"running\", \"finished\", \"interrupted\". Any other string means the persisted turn state is outside the known state machine, so the runtime cannot be safely resumed.","triggerScenarios":"Loading a session whose runtime JSON has activeTurn.state equal to something other than running/finished/interrupted (e.g. \"pending\", \"done\", wrong casing, empty); a runtime file written by a different or newer kernel build with an extended state vocabulary.","commonSituations":"Downgraded or mismatched kernel versions; manual edits; a bug in code that writes turn state without going through the state machine.","solutions":["Correct the state to one of running, finished, or interrupted in the runtime file (prefer finished/interrupted to avoid resuming)","Restore the runtime file from a backup made by a compatible kernel version","Align all kernel installations to the same version so the state vocabulary matches"],"exampleFix":"// before (runtime JSON)\n\"activeTurn\": {\"turnID\": \"t1\", \"state\": \"in-progress\"}\n// after\n\"activeTurn\": {\"turnID\": \"t1\", \"state\": \"interrupted\"}","handlingStrategy":"validation","validationCode":"const TURN_STATES = new Set([\"running\", \"finished\", \"interrupted\"]);\nif (runtime.activeTurn && !TURN_STATES.has(runtime.activeTurn.state)) throw new Error(`unknown turn state: ${runtime.activeTurn.state}`);","typeGuard":"function isTurnState(s: unknown): s is TurnState { return s === \"running\" || s === \"finished\" || s === \"interrupted\"; }","tryCatchPattern":null,"preventionTips":["Model turn state as an enum in client code so invalid values cannot be written","Match the kernel version when syncing workspaces so the state vocabulary agrees","On mismatch, map unknown states to \"interrupted\" during migration"],"tags":["agent","state-machine","enum"],"backgroundTag":"invalid-enum-value","analyzedSha":"9f775e8a12daef8255556097396f9b2739078892","analyzedAt":"2026-09-19T03:17:15.984Z","contentChangedAt":"2026-09-19T03:17:15.984Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}