mastra-ai/mastra · error

Task ID is required

Error message

Task ID is required

What it means

The in-memory A2A task store's save() requires a task `id` because it derives the storage key from (agentId, data.id). Saving a task object without an id cannot be keyed, so it throws 'Task ID is required' defensively before mutating the store.

Source

Thrown at packages/server/src/server/a2a/store.ts:63

      version: this.versions.get(key) ?? 0,
    };
  }

  async save({
    agentId,
    data,
    expectedVersion,
    skipIfCanceled = false,
  }: {
    agentId: string;
    data: Task;
    expectedVersion?: number;
    skipIfCanceled?: boolean;
  }): Promise<Task> {
    // Store copies to prevent internal mutation if caller reuses objects
    const key = this.getKey(agentId, data.id);
    if (!data.id) {
      throw new Error('Task ID is required');
    }

    const existingTask = this.store.get(key);

    if (skipIfCanceled && existingTask?.status.state === 'canceled' && data.status.state !== 'canceled') {
      return { ...existingTask };
    }

    const currentVersion = this.versions.get(key) ?? 0;
    if (expectedVersion !== undefined && currentVersion !== expectedVersion) {
      throw new TaskStoreVersionConflictError(expectedVersion, currentVersion);
    }

    const storedTask = { ...data };
    const nextVersion = currentVersion + 1;

    this.store.set(key, storedTask);
    this.versions.set(key, nextVersion);

View on GitHub (pinned to 75dd419e61)

Solutions

  1. Ensure every task passed to save() has a non-empty `id` string
  2. Generate an id (crypto.randomUUID()) when creating tasks programmatically
  3. Check request parsing — confirm the incoming message/params actually carry `id` and it isn't renamed
  4. Add a schema check in your ingestion layer before calling save

Example fix

// before
await store.save(agentId, { status: { state: 'working' } })
// after
await store.save(agentId, { id: crypto.randomUUID(), status: { state: 'working' } })
Defensive patterns

Strategy: validation

Validate before calling

function assertTaskId(task: { id?: string }) {
  if (!task.id || typeof task.id !== 'string') {
    throw new Error('Task must have a non-empty string id before save');
  }
}

Type guard

const hasId = (t: { id?: string }): t is { id: string } =>
  typeof t.id === 'string' && t.id.length > 0;

Try / catch

try {
  await store.save(agentId, task);
} catch (err) {
  if (err instanceof Error && err.message === 'Task ID is required') {
    logger.error('Attempted to persist task without id', { task });
  } else throw err;
}

Prevention

When it happens

Trigger: Calling store.save(agentId, task) where task.id is undefined/empty — e.g. constructing a Task literal manually and forgetting id, or building a task from an incoming request whose params lack `id`.

Common situations: Custom A2A integrations creating tasks programmatically, middleware that strips fields, deserialization bugs where `id` was named `taskId`, or tests seeding tasks with partial fixtures.

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/cb662c427447d035. Report an issue: GitHub.