{"record":{"id":"af87a90662c2070a","repo":"mastra-ai/mastra","slug":"notification-summary-is-missing-agentid","errorCode":null,"errorMessage":"Notification summary is missing agentId","messagePattern":"Notification summary is missing agentId","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/core/src/notifications/dispatcher.ts","lineNumber":194,"sourceCode":"    deliveredSignalId: result.signal.id,\n    lastDeliveryAttemptAt: now,\n  });\n  return { record: updated, signal: result.signal };\n}\n\nasync function sendNotificationSummary({\n  mastra,\n  storage,\n  records,\n  now,\n}: {\n  mastra: Mastra;\n  storage: NotificationsStorage;\n  records: NotificationRecord[];\n  now: Date;\n}): Promise<{ records: NotificationRecord[]; signal: CreatedAgentSignal }> {\n  const first = records[0];\n  if (!first?.agentId) throw new Error('Notification summary is missing agentId');\n  if (!first.resourceId) throw new Error('Notification summary is missing resourceId');\n\n  const agent = (await mastra.getAgentById(first.agentId as never)) as NotificationDispatchAgent;\n  const summary = summarizeNotifications(records);\n  const signal = createNotificationSummarySignal(summary);\n  // The all-low-priority batch persists without waking, so it never starts a\n  // run and needs no stream options.\n  const allLowPriority = records.every(record => record.priority === 'low');\n  const streamOptions = allLowPriority\n    ? undefined\n    : await resolveIfIdleStreamOptions(mastra, agent, {\n        record: first,\n        threadState: agentThreadStreamRuntime.getThreadState(\n          { resourceId: first.resourceId, threadId: first.threadId },\n          agent.getPubSub?.(),\n        ),\n        now,\n      });","sourceCodeStart":176,"sourceCodeEnd":212,"githubUrl":"https://github.com/mastra-ai/mastra/blob/75dd419e613fe9c39f846ffc500716141b74fda6/packages/core/src/notifications/dispatcher.ts#L176-L212","documentation":"sendNotificationSummary batches multiple pending notifications into one summary signal delivered through the first record's agent. This error is thrown when the first record of a batch has no `agentId`, which is required to resolve the agent that receives the summary. Like the per-record variant, it signals a corrupted or non-standard record in storage.","triggerScenarios":"A low-priority batch flush calls sendNotificationSummary with records[0] missing or having an empty agentId — e.g. an empty/undefined records array (first is undefined), or a persisted record created without agentId reaching the summary path.","commonSituations":"Batching logic grouping records where the anchor record was written outside the official API; migrations or older versions dropping agentId; custom storage adapters losing fields; a bug in batch grouping that includes undefined entries.","solutions":["Ensure all notifications are created through the official API so agentId is always set on every record in a batch.","Backfill agentId on stored records missing it, or drop them from the batch.","Guard batching code so records arrays passed to the summary path are non-empty and validated (filter out records without agentId before flushing).","Audit your storage adapter/migrations for writes that omit agentId."],"exampleFix":"// before\nawait flushSummary(allPendingRecords); // may contain records without agentId\n// after\nconst deliverable = allPendingRecords.filter(r => r.agentId && r.resourceId);\nif (deliverable.length) await flushSummary(deliverable);","handlingStrategy":"validation","validationCode":"if (!records.length || !records[0]?.agentId) {\n  logger.warn('Skipping summary batch: first record missing or lacks agentId');\n  return;\n}","typeGuard":"function isSummarizable(records: NotificationRecord[]): records is [NotificationRecord & { agentId: string; resourceId: string }, ...NotificationRecord[]] {\n  const first = records[0] as (NotificationRecord & { agentId?: string; resourceId?: string }) | undefined;\n  return !!first && typeof first.agentId === 'string' && typeof first.resourceId === 'string';\n}","tryCatchPattern":"try {\n  await sendNotificationSummary({ mastra, storage, records, now });\n} catch (err) {\n  if (err instanceof Error && err.message === 'Notification summary is missing agentId') {\n    logger.error('Summary batch contained a record without agentId; falling back to per-record delivery', { err });\n    await Promise.all(records.map(r => sendNotificationRecord({ mastra, storage, record: r, now })));\n  } else {\n    throw err;\n  }\n}","preventionTips":["Filter batches to records with both agentId and resourceId before summarizing.","Never push undefined/null entries into batch record arrays.","Create notifications only through official APIs so agentId is guaranteed.","Add an integration test that flushes a low-priority batch with mixed records."],"tags":["notifications","storage","batching","data-integrity"],"backgroundTag":"notification-missing-agent-id","analyzedSha":"75dd419e613fe9c39f846ffc500716141b74fda6","analyzedAt":"2026-08-30T00:15:31.844Z","schemaVersion":2},"datasetVersion":"2026-08-30T03:17:51.788Z"}