siyuan-note/siyuan · error

agent runtime session id mismatch

Error message

agent runtime session id mismatch

What it means

Rejected by siyuan.rpc.unbind(name) when an argument is supplied but is not a string (kernel/plugin/api_rpc.go:109). The same goja.IsString check as bind is applied to the method name; numbers, objects, or undefined-after-optional-chaining values all land here. The lookup key must exactly match the string used at bind time.

Source

Thrown at kernel/agent/runtime.go:207

}

func loadRuntimeLocked(sessionID string) (*agentRuntime, error) {
	data, err := os.ReadFile(runtimePath(sessionID))
	if err != nil {
		if os.IsNotExist(err) {
			return &agentRuntime{SchemaVersion: 1, SessionID: sessionID}, nil
		}
		return nil, err
	}
	var runtime agentRuntime
	if err := gulu.JSON.UnmarshalJSON(data, &runtime); err != nil {
		return nil, err
	}
	if runtime.SchemaVersion > 1 {
		return nil, fmt.Errorf("unsupported agent runtime schema version: %d", runtime.SchemaVersion)
	}
	if runtime.SessionID != "" && runtime.SessionID != sessionID {
		return nil, fmt.Errorf("agent runtime session id mismatch")
	}
	if runtime.Revision < 0 {
		return nil, fmt.Errorf("invalid agent runtime revision")
	}
	if runtime.ActiveTurn != nil {
		if runtime.ActiveTurn.TurnID == "" {
			return nil, fmt.Errorf("invalid agent runtime turn id")
		}
		switch runtime.ActiveTurn.State {
		case "running", "finished", "interrupted":
		default:
			return nil, fmt.Errorf("invalid agent runtime turn state")
		}
	}
	if runtime.SchemaVersion == 0 {
		runtime.SchemaVersion = 1
	}
	if runtime.SessionID == "" {

View on GitHub (pinned to afa823b6b4)

Solutions

  1. Pass the exact string used in bind: await siyuan.rpc.unbind('myplugin.echo')
  2. If you store records, unbind record.name, not the record
  3. Keep a single constant for each method name used by both bind and unbind

Example fix

// before
await siyuan.rpc.unbind(handler); // function, not name

// after
await siyuan.rpc.unbind('myplugin.echo');
Defensive patterns

Strategy: type-guard

Validate before calling

await siyuan.rpc.unbind(typeof rec === 'string' ? rec : rec.name);

Type guard

const isRpcName = (v) => typeof v === 'string' && v.length > 0;

Try / catch

try { await siyuan.rpc.unbind(value); } catch (e) { if (/first argument must be method name string/.test(e.message)) await siyuan.rpc.unbind(value.name ?? String(value)); else throw e; }

Prevention

When it happens

Trigger: unbind(handlerFn) passing the function instead of its name; unbind(3) for an index-built name; unbind(registrations[i]) where the entry is an object like {name, fn}.

Common situations: Symmetric APIs in other libraries whose unbind takes the function reference; storing registration records instead of plain names; renaming methods at bind time but unbinding the old symbol.

Understand the failure class

Background: Invalid argument type errors: "must be of type string", "expected X, got Y", and ERR_INVALID_ARG_TYPE explained — this error's family across 15 libraries.

Related errors


AI-assisted analysis of siyuan-note/siyuan@afa823b6b4 (2026-08-18). Data as JSON: /api/errors/c3eba555c4cf280d. Report an issue: GitHub.