{"record":{"id":"cb662c427447d035","repo":"mastra-ai/mastra","slug":"task-id-is-required","errorCode":null,"errorMessage":"Task ID is required","messagePattern":"Task ID is required","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/server/src/server/a2a/store.ts","lineNumber":63,"sourceCode":"      version: this.versions.get(key) ?? 0,\n    };\n  }\n\n  async save({\n    agentId,\n    data,\n    expectedVersion,\n    skipIfCanceled = false,\n  }: {\n    agentId: string;\n    data: Task;\n    expectedVersion?: number;\n    skipIfCanceled?: boolean;\n  }): Promise<Task> {\n    // Store copies to prevent internal mutation if caller reuses objects\n    const key = this.getKey(agentId, data.id);\n    if (!data.id) {\n      throw new Error('Task ID is required');\n    }\n\n    const existingTask = this.store.get(key);\n\n    if (skipIfCanceled && existingTask?.status.state === 'canceled' && data.status.state !== 'canceled') {\n      return { ...existingTask };\n    }\n\n    const currentVersion = this.versions.get(key) ?? 0;\n    if (expectedVersion !== undefined && currentVersion !== expectedVersion) {\n      throw new TaskStoreVersionConflictError(expectedVersion, currentVersion);\n    }\n\n    const storedTask = { ...data };\n    const nextVersion = currentVersion + 1;\n\n    this.store.set(key, storedTask);\n    this.versions.set(key, nextVersion);","sourceCodeStart":45,"sourceCodeEnd":81,"githubUrl":"https://github.com/mastra-ai/mastra/blob/75dd419e613fe9c39f846ffc500716141b74fda6/packages/server/src/server/a2a/store.ts#L45-L81","documentation":"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.","triggerScenarios":"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`.","commonSituations":"Custom A2A integrations creating tasks programmatically, middleware that strips fields, deserialization bugs where `id` was named `taskId`, or tests seeding tasks with partial fixtures.","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"],"exampleFix":"// before\nawait store.save(agentId, { status: { state: 'working' } })\n// after\nawait store.save(agentId, { id: crypto.randomUUID(), status: { state: 'working' } })","handlingStrategy":"validation","validationCode":"function assertTaskId(task: { id?: string }) {\n  if (!task.id || typeof task.id !== 'string') {\n    throw new Error('Task must have a non-empty string id before save');\n  }\n}","typeGuard":"const hasId = (t: { id?: string }): t is { id: string } =>\n  typeof t.id === 'string' && t.id.length > 0;","tryCatchPattern":"try {\n  await store.save(agentId, task);\n} catch (err) {\n  if (err instanceof Error && err.message === 'Task ID is required') {\n    logger.error('Attempted to persist task without id', { task });\n  } else throw err;\n}","preventionTips":["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"],"tags":["a2a","validation","tasks","missing-id"],"backgroundTag":"missing-required-field","analyzedSha":"75dd419e613fe9c39f846ffc500716141b74fda6","analyzedAt":"2026-08-30T00:15:31.844Z","schemaVersion":2},"datasetVersion":"2026-08-30T03:17:51.788Z"}