{"record":{"id":"89e0c0e8ea399221","repo":"siyuan-note/siyuan","slug":"invalid-agent-runtime-turn","errorCode":null,"errorMessage":"invalid agent runtime turn","messagePattern":"invalid agent runtime turn","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"kernel/agent/runtime.go","lineNumber":257,"sourceCode":"\truntime.SchemaVersion = 1\n\truntime.SessionID = sessionID\n\truntime.Revision++\n\tdata, err := gulu.JSON.MarshalIndentJSON(runtime, \"\", \"\\t\")\n\tif err != nil {\n\t\treturn err\n\t}\n\treturn filelock.WriteFile(runtimePath(sessionID), data)\n}\n\nfunc beginRuntimeTurn(sessionID string, turn *agentRuntimeTurn) error {\n\tif sessionID == \"\" || turn == nil {\n\t\treturn nil\n\t}\n\tif !isValidSessionID(sessionID) {\n\t\treturn fmt.Errorf(\"invalid session id\")\n\t}\n\tif turn.TurnID == \"\" || turn.State != \"running\" {\n\t\treturn fmt.Errorf(\"invalid agent runtime turn\")\n\t}\n\tlock := sessionLock(sessionID)\n\tlock.Lock()\n\tdefer lock.Unlock()\n\truntime, err := loadRuntimeLocked(sessionID)\n\tif err != nil {\n\t\treturn err\n\t}\n\tif runtime.ActiveTurn != nil && runtime.ActiveTurn.TurnID != turn.TurnID {\n\t\tcommitted, err := isTurnCommittedLocked(sessionID, runtime.ActiveTurn.TurnID)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif !committed {\n\t\t\treturn fmt.Errorf(\"agent session has an uncommitted turn\")\n\t\t}\n\t\truntime.ActiveTurn = nil\n\t}","sourceCodeStart":239,"sourceCodeEnd":275,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/8641553a1f07374001902d3ce773285db1292b2d/kernel/agent/runtime.go#L239-L275","documentation":"beginRuntimeTurn rejects a turn that has no TurnID or whose State is not exactly \"running\" with 'invalid agent runtime turn'. Only a fully-formed, running turn may become the session's ActiveTurn. This enforces the runtime's turn state machine invariant.","triggerScenarios":"Passing a turn struct that was never initialized (TurnID == \"\"), or one whose State is a non-running value such as \"pending\", \"committed\", or \"failed\" into beginRuntimeTurn.","commonSituations":"Reusing a turn object after it was committed or finalized; constructing agentRuntimeTurn by hand without setting TurnID/State; resuming a persisted turn snapshot whose state is terminal.","solutions":["Ensure TurnID is assigned (via the runtime's turn-creation helper) before calling beginRuntimeTurn","Verify turn.State == \"running\" prior to the call; transition through the proper state machine instead of passing terminal turns","If reusing a persisted turn, reload it and check its state; create a new turn for new work"],"exampleFix":"// before\nturn := &agentRuntimeTurn{State: \"pending\"}\nbeginRuntimeTurn(sessionID, turn) // rejected\n// after\nturn := newRuntimeTurn()\nturn.State = \"running\"\nbeginRuntimeTurn(sessionID, turn)","handlingStrategy":"validation","validationCode":"if turn == nil || turn.TurnID == \"\" || turn.State != \"running\" { return errors.New(\"turn must be initialized and running\") }","typeGuard":"func isRunnableTurn(t *agentRuntimeTurn) bool { return t != nil && t.TurnID != \"\" && t.State == \"running\" }","tryCatchPattern":null,"preventionTips":["Create turns only via the runtime's turn-construction helpers","Never pass a committed/terminal turn back into beginRuntimeTurn","Assert turn state in tests before starting turns"],"tags":["go","agent-runtime","state-machine"],"backgroundTag":"invalid-argument-value","analyzedSha":"8641553a1f07374001902d3ce773285db1292b2d","analyzedAt":"2026-09-11T16:08:28.414Z","contentChangedAt":"2026-09-11T16:08:28.414Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}