mastra-ai/mastra · error

resourceId and threadId are required to send a state signal

Error message

resourceId and threadId are required to send a state signal

What it means

AgentThreadStreamRuntime.sendStateSignal() throws this when target.resourceId or target.threadId is missing. Unlike queueMessage, sendStateSignal performs no inference from active run records — the caller must supply both IDs up front so the state signal can be persisted to the correct memory thread.

Source

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

      };
    }

    return this.sendSignal<OUTPUT>(
      agent,
      signal,
      { ...target, runId, resourceId, threadId, ifIdle: { ...target.ifIdle, behavior: 'wake' } },
      pubsub,
    );
  }

  async sendStateSignal<OUTPUT = unknown>(
    agent: Agent<any, any, any, any>,
    stateInput: AgentStateSignalInput,
    target: SendAgentStateSignalOptions<OUTPUT>,
    pubsub?: PubSub,
  ): Promise<SendAgentStateSignalResult<OUTPUT>> {
    if (!target.resourceId || !target.threadId) {
      throw new Error('resourceId and threadId are required to send a state signal');
    }
    const resourceId = target.resourceId;
    const threadId = target.threadId;

    const requestContext = target.ifIdle?.streamOptions?.requestContext;
    const memoryContext = parseMemoryRequestContext(requestContext);
    const memory = await agent.getMemory({ requestContext });
    if (!memory) {
      throw new Error('sendStateSignal requires Mastra memory');
    }

    const loadedThread = (await memory.getThreadById({ threadId })) ?? memoryContext?.thread;
    if (!loadedThread) {
      throw new Error(`sendStateSignal could not load thread ${threadId}`);
    }

    const thread = {
      ...loadedThread,

View on GitHub (pinned to 75dd419e61)

Solutions

  1. Pass both resourceId and threadId in the target options before calling sendStateSignal.
  2. Add a pre-call guard that asserts both IDs are non-empty strings.
  3. If the IDs come from a request/session object, validate them at the API boundary instead of at signal time.

Example fix

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

Strategy: validation

Validate before calling

if (!target?.resourceId || !target?.threadId) {
  throw new Error('sendStateSignal requires resourceId and threadId');
}

Type guard

function isStateSignalTarget<T>(t: SendAgentStateSignalOptions<T>): t is SendAgentStateSignalOptions<T> & { resourceId: string; threadId: string } {
  return Boolean(t.resourceId && t.threadId);
}

Try / catch

try {
  return await runtime.sendStateSignal(agent, state, target);
} catch (e) {
  if (e instanceof Error && e.message.includes('required to send a state signal')) {
    // surface a 400-style validation error to the caller
  }
  throw e;
}

Prevention

When it happens

Trigger: Calling runtime.sendStateSignal(agent, stateInput, target) with resourceId or threadId undefined/null in the SendAgentStateSignalOptions target.

Common situations: Partially constructed options objects when routing signals dynamically; type-looseness letting { resourceId } without threadId compile; migrating code that previously targeted an active run by runId only.

Related errors


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