siyuan-note/siyuan · error

invalid agent runtime turn

Error message

invalid agent runtime turn

What it means

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.

Source

Thrown at kernel/agent/runtime.go:257

	runtime.SchemaVersion = 1
	runtime.SessionID = sessionID
	runtime.Revision++
	data, err := gulu.JSON.MarshalIndentJSON(runtime, "", "\t")
	if err != nil {
		return err
	}
	return filelock.WriteFile(runtimePath(sessionID), data)
}

func beginRuntimeTurn(sessionID string, turn *agentRuntimeTurn) error {
	if sessionID == "" || turn == nil {
		return nil
	}
	if !isValidSessionID(sessionID) {
		return fmt.Errorf("invalid session id")
	}
	if turn.TurnID == "" || turn.State != "running" {
		return fmt.Errorf("invalid agent runtime turn")
	}
	lock := sessionLock(sessionID)
	lock.Lock()
	defer lock.Unlock()
	runtime, err := loadRuntimeLocked(sessionID)
	if err != nil {
		return err
	}
	if runtime.ActiveTurn != nil && runtime.ActiveTurn.TurnID != turn.TurnID {
		committed, err := isTurnCommittedLocked(sessionID, runtime.ActiveTurn.TurnID)
		if err != nil {
			return err
		}
		if !committed {
			return fmt.Errorf("agent session has an uncommitted turn")
		}
		runtime.ActiveTurn = nil
	}

View on GitHub (pinned to 8641553a1f)

Solutions

  1. Ensure TurnID is assigned (via the runtime's turn-creation helper) before calling beginRuntimeTurn
  2. Verify turn.State == "running" prior to the call; transition through the proper state machine instead of passing terminal turns
  3. If reusing a persisted turn, reload it and check its state; create a new turn for new work

Example fix

// before
turn := &agentRuntimeTurn{State: "pending"}
beginRuntimeTurn(sessionID, turn) // rejected
// after
turn := newRuntimeTurn()
turn.State = "running"
beginRuntimeTurn(sessionID, turn)
Defensive patterns

Strategy: validation

Validate before calling

if turn == nil || turn.TurnID == "" || turn.State != "running" { return errors.New("turn must be initialized and running") }

Type guard

func isRunnableTurn(t *agentRuntimeTurn) bool { return t != nil && t.TurnID != "" && t.State == "running" }

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Understand the failure class

Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.

Related errors


AI-assisted analysis of siyuan-note/siyuan@8641553a1f (2026-09-11). Data as JSON: /api/errors/89e0c0e8ea399221. Report an issue: GitHub.