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
- Ensure every task passed to save() has a non-empty `id` string
- Generate an id (crypto.randomUUID()) when creating tasks programmatically
- Check request parsing — confirm the incoming message/params actually carry `id` and it isn't renamed
- 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
- Generate ids (crypto.randomUUID()) at task creation, never at save time
- Type tasks with a required `id: string` field so TS catches omissions
- Validate incoming A2A request params include `id` before mapping to a Task
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
- OBSERVABILITY_MISSING_RECORD_ID
- ClaudeSDKAgent resumeData must include sessionId or continue
- CursorSDKAgent resumeData must include a message.
- CursorSDKAgent resumeData.agentId must be a string when prov
- CursorSDKAgent does not support structuredOutput because the
AI-assisted analysis of mastra-ai/mastra@75dd419e61 (2026-08-30).
Data as JSON: /api/errors/cb662c427447d035.
Report an issue: GitHub.