mastra-ai/mastra · error · ChannelSessionRejectedError

ChannelSessionRejectedError

Error message

ChannelSessionRejectedError

What it means

ChannelSessionRejectedError wraps any failure thrown by the user-configured `resolveSession` hook when AgentControllerChannels tries to establish a channel session. The library re-throws the original cause inside this error so channel code can uniformly detect session resolution failures. It indicates the session factory/hook supplied in AgentControllerConfig rejected (threw or returned a rejected promise).

Source

Thrown at packages/core/src/channels/agent-controller-channels.ts:454

    // Bind the mapped thread. Guard is mandatory: `switch` aborts any active
    // run, so never re-switch when the session is already on this thread.
    if (session.thread.getId() !== thread.id) {
      await session.thread.switch({ threadId: thread.id });
    }
    await this.runSessionStartHook(session, thread, requestContext);
    return session;
  }

  /**
   * Call the host's resolver and tag anything it throws as a refusal. Covers
   * synchronous throws too — the hook may return a `Session` directly, so a
   * plain `.catch()` on the return value would miss them.
   */
  private async resolveChannelSession(ctx: ChannelSessionResolveContext): Promise<Session<any>> {
    try {
      return await this.resolveSession!(ctx);
    } catch (cause) {
      throw new ChannelSessionRejectedError(cause);
    }
  }

  /**
   * Run the configured session-start hook at most once per session. Fires after
   * the thread binding above, so a hook that persists session settings writes
   * them to the thread the session actually ends up on.
   */
  private async runSessionStartHook(
    session: Session<any>,
    thread: Pick<StorageThreadType, 'id' | 'resourceId'>,
    requestContext?: RequestContext,
  ): Promise<void> {
    const onSessionStart = this.onSessionStart;
    if (!onSessionStart) return;
    // Memoize before the first await: two messages arriving together on a new
    // thread both reach this point. The first starts the hook; every later
    // caller awaits the same run, so no message dispatches before the session

View on GitHub (pinned to 75dd419e61)

Solutions

  1. Inspect error.cause for the original exception thrown by your resolveSession implementation and fix that root cause first.
  2. Ensure your resolveSession function is async-safe: wrap its internal awaits in try/catch or make sure it cannot throw synchronously.
  3. Verify any storage/Mastra instance your resolveSession depends on is configured and reachable.
  4. Add logging inside your resolveSession to pinpoint which step rejects.

Example fix

// before
channels: { resolveSession: async (ctx) => lookupThread(ctx.threadId) } // lookupThread may throw
// after
channels: { resolveSession: async (ctx) => { try { return await lookupThread(ctx.threadId); } catch (e) { logger.error('session resolve failed', e); throw e; } } }
Defensive patterns

Strategy: try-catch

Validate before calling

if (typeof config.resolveSession !== 'function') throw new Error('resolveSession must be provided');

Type guard

function hasCause(e: unknown): e is { cause: unknown } { return typeof e === 'object' && e !== null && 'cause' in e; }

Try / catch

try { await channelsSession(); } catch (e) { const cause = hasCause(e) ? e.cause : e; logger.error('channel session rejected', cause); }

Prevention

When it happens

Trigger: Calling `session` (or any channel flow that calls resolveChannelSession) when the configured `resolveSession` function in AgentControllerConfig throws — e.g. it fails to look up a thread, hits a storage/backend error, or has a bug.

Common situations: Custom resolveSession implementations that query storage that is down or unconfigured; async session factories that reject on network failures; typos in session resolution logic after refactors.

Related errors


AI-assisted analysis of mastra-ai/mastra@75dd419e61 (2026-08-30). Data as JSON: /api/errors/56a72755935f5071. Report an issue: GitHub.