Skip to content

[Bug]: Session restoration has implicit startup race in SessionManager hydration #1889

@amanharshx

Description

@amanharshx

Bug Description

SessionManager fires async hydration (loadActiveSessions()) in its constructor without awaiting it. ChannelManager.initialize() then immediately passes the unhydrated session manager to PluginManager and starts loading plugins. The in-memory session map may be empty when plugins begin accepting messages, causing getSession() to miss existing persisted sessions.

Steps to Reproduce

  1. Have an active channel session persisted in the database (e.g., a Telegram user mid-conversation)
  2. Restart the app
  3. Send a message via the channel plugin immediately after startup
  4. Observe that a new session/conversation is created instead of resuming the existing one

Note: this does not reproduce reliably today because shared getDatabase() promise ordering implicitly guarantees hydration completes first. It becomes reproducible if the DB instance is cached or startup order is refactored.

Root Cause

The SessionManager constructor calls this.loadActiveSessions() (an async method) but does not store or await the returned promise. ChannelManager.initialize() instantiates SessionManager and immediately proceeds to create PluginManager and load plugins — no barrier ensures hydration has finished.

The current code is safe only because both loadActiveSessions() and loadEnabledPlugins() internally await getDatabase(), and microtask ordering guarantees the hydration continuation runs first. This is implicit and fragile.

Impact if broken

  • getSession() returns null for users with existing sessions
  • Duplicate sessions and conversations are created
  • Users lose conversation history after app restarts
  • Orphaned DB records accumulate over time
  • Tool confirmation callbacks may route to the wrong conversation

Affected Files (fixed in #1888)

  • src/process/channels/core/SessionManager.ts — constructor fires hydration without storing promise
  • src/process/channels/core/ChannelManager.ts — no await on session hydration before plugin setup

Diff

+5 −1 across 2 files (explicit await added, no logic changes)

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions