mastra-ai/mastra · error · MastraError

SCHEDULES_MISSING_RESOURCE_ID

SCHEDULES_MISSING_RESOURCE_ID

Error message

schedules.create requires `resourceId` when `threadId` is set.

What it means

Like definition-time validation, the runtime `create` path (`#createAgentSchedule`, packages/core/src/schedules/schedules.ts:285) rejects an agent schedule that sets `threadId` without `resourceId`. A memory thread is always scoped to a resource, so `SCHEDULES_MISSING_RESOURCE_ID` is thrown at create time with HTTP-style status 400 rather than allowing an unresolvable thread reference to be stored.

Source

Thrown at packages/core/src/schedules/schedules.ts:285

    }
    return this.#createAgentSchedule(input as CreateAgentScheduleInput);
  }

  async #createAgentSchedule(input: CreateAgentScheduleInput): Promise<AgentSchedule> {
    validateCron(input.cron, input.timezone);

    if (!input.agentId) {
      throw new MastraError({
        id: 'SCHEDULES_MISSING_TARGET_ID',
        domain: ErrorDomain.AGENT,
        category: ErrorCategory.USER,
        details: { status: 400 },
        text: 'schedules.create requires `agentId` or `workflowId`.',
      });
    }

    if (input.threadId && !input.resourceId) {
      throw new MastraError({
        id: 'SCHEDULES_MISSING_RESOURCE_ID',
        domain: ErrorDomain.AGENT,
        category: ErrorCategory.USER,
        details: { status: 400 },
        text: 'schedules.create requires `resourceId` when `threadId` is set.',
      });
    }
    if (!input.threadId) {
      const offenders: string[] = [];
      if (input.signalType !== undefined) offenders.push('signalType');
      if (input.ifActive !== undefined) offenders.push('ifActive');
      if (input.ifIdle !== undefined) offenders.push('ifIdle');
      if (input.resourceId !== undefined) offenders.push('resourceId');
      if (offenders.length > 0) {
        throw new MastraError({
          id: 'SCHEDULES_THREADLESS_OPTIONS',
          domain: ErrorDomain.AGENT,
          category: ErrorCategory.USER,

View on GitHub (pinned to 75dd419e61)

Solutions

  1. Pass `resourceId` alongside `threadId` in the create input.
  2. Drop `threadId` if the schedule does not need a memory thread.
  3. Centralize schedule-creation inputs through a validated factory that enforces threadId ⇒ resourceId.

Example fix

// before
await schedules.create({
  agentId: 'assistant',
  prompt: 'Summarize the conversation',
  cron: '0 9 * * *',
  threadId: 'thread_123',
});

// after
await schedules.create({
  agentId: 'assistant',
  prompt: 'Summarize the conversation',
  cron: '0 9 * * *',
  threadId: 'thread_123',
  resourceId: 'user_42',
});
Defensive patterns

Strategy: validation

Validate before calling

function canCreateAgentSchedule(input) {
  return Boolean(input.agentId) && !(input.threadId && !input.resourceId);
}
if (!canCreateAgentSchedule(input)) {
  throw new Error('resourceId is required when threadId is set');
}

Type guard

function hasResourceForThread(input: { threadId?: string; resourceId?: string }):
  input is typeof input & { resourceId: string } {
  return !input.threadId || (typeof input.resourceId === 'string' && input.resourceId.length > 0);
}

Try / catch

try {
  await schedules.create(input);
} catch (e) {
  if (e instanceof MastraError && e.id === 'SCHEDULES_MISSING_RESOURCE_ID') {
    throw new Error('Provide resourceId alongside threadId for this agent schedule');
  }
  throw e;
}

Prevention

When it happens

Trigger: Calling `schedules.create({ agentId, prompt, cron, threadId: '...' })` with no `resourceId` — the `input.threadId && !input.resourceId` check throws immediately after the target-id and cron checks pass.

Common situations: Attaching conversation memory to scheduled agent prompts and copying only the threadId from a previous run; dynamic inputs where resourceId is conditionally undefined; teams adding thread persistence to an existing schedule and forgetting the resource field.

Understand the failure class

Background: "Missing required field" and "field is required" errors: why libraries reject payloads that omit mandatory fields — this error's family across 20 libraries.

Related errors


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