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
- Pass the exact string used in bind: await siyuan.rpc.unbind('myplugin.echo')
- If you store records, unbind record.name, not the record
- 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
- Unbind by the exact string used at bind time
- Store plain name strings, not {name, fn} records, in the unload list
- Centralize method names in one constants module
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
- invalid agent runtime turn id
- invalid agent runtime revision
- invalid agent runtime turn state
- agent runtime user entry not found
- first argument must be a tool name string
AI-assisted analysis of siyuan-note/siyuan@afa823b6b4 (2026-08-18).
Data as JSON: /api/errors/c3eba555c4cf280d.
Report an issue: GitHub.