mastra-ai/mastra · error

resourceId and threadId are required to queue a message

Error message

resourceId and threadId are required to queue a message

What it means

AgentThreadStreamRuntime.queueMessage() throws this when it cannot resolve both a resourceId and a threadId for the signal target. Normally these come from the options object, but when omitted the runtime tries to infer them from an active run record (looked up by runId or thread key). If neither the options nor an active record yields both IDs, it cannot address a thread and throws.

Source

Thrown at packages/core/src/agent/thread-stream-runtime.ts:2670

      activeRecord = activeRunId ? state.threadRunsById.get(activeRunId) : undefined;
      if (activeRecord && !this.#isThreadBlockingRun(state, activeRecord)) {
        state.activeThreadRunIds.delete(key);
        activeRecord = undefined;
      }
      runId ??= activeRunId;
    }

    if (runId) {
      activeRecord ??= state.threadRunsById.get(runId);
      if (activeRecord) {
        key ??= this.#threadKey(activeRecord.resourceId, activeRecord.threadId);
      }
    }

    const resourceId = target.resourceId ?? activeRecord?.resourceId;
    const threadId = target.threadId ?? activeRecord?.threadId;
    if (!resourceId || !threadId) {
      throw new Error('resourceId and threadId are required to queue a message');
    }

    key ??= this.#threadKey(resourceId, threadId);
    const signal = createMessageSignal(message, {
      id: this.#generateSignalMessageId(agent, { resourceId, threadId }),
      acceptedAt,
    });
    const queuedRunId = randomUUID();
    const queuedStreamOptions = target.ifIdle?.streamOptions ?? activeRecord?.streamOptions;

    if (activeRecord) {
      const idleQueue = state.pendingIdleSignalsByThread.get(key) ?? [];
      idleQueue.push({ agent, signal, runId: queuedRunId, resourceId, threadId, streamOptions: queuedStreamOptions });
      state.pendingIdleSignalsByThread.set(key, idleQueue);
      this.#watchThreadRunCompletion(state, pubsub, key, activeRecord);
      return {
        signal,
        accepted: Promise.resolve({ action: 'deliver' as const, runId: queuedRunId }),

View on GitHub (pinned to 75dd419e61)

Solutions

  1. Always pass both resourceId and threadId in the QueueAgentMessageOptions target object.
  2. If you only have a runId, verify the run is still active (registered in the runtime state) before calling queueMessage, or capture resourceId/threadId when the run started.
  3. Wrap the call in try/catch and fall back to agent.generate/stream directly when no thread context exists.

Example fix

// before
await runtime.queueMessage(agent, message, {});
// after
await runtime.queueMessage(agent, message, { resourceId: 'user-1', threadId: 'thread-1' });
Defensive patterns

Strategy: validation

Validate before calling

function canQueueMessage(target) {
  return Boolean(target.resourceId && target.threadId);
}
if (!canQueueMessage(target)) throw new Error('queueMessage needs resourceId and threadId');

Type guard

function hasThreadTarget(t: { resourceId?: string; threadId?: string }): t is { resourceId: string; threadId: string } {
  return typeof t.resourceId === 'string' && t.resourceId.length > 0 && typeof t.threadId === 'string' && t.threadId.length > 0;
}

Try / catch

try {
  return runtime.queueMessage(agent, message, target);
} catch (e) {
  if (e instanceof Error && e.message.includes('required to queue a message')) {
    // no resolvable thread context — fall back to direct generation or rethrow with context
  }
  throw e;
}

Prevention

When it happens

Trigger: Calling runtime.queueMessage(agent, message, target) with (a) neither target.resourceId nor target.threadId set, and (b) no target.runId matching an active run record whose resourceId/threadId can be inferred.

Common situations: Building a custom pubsub/signal integration and passing only runId after the run already completed (record evicted from state); copying options objects and dropping resourceId/threadId; calling queueMessage from a context where only a message is available.

Related errors


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