{"record":{"id":"b6425c6e32914af3","repo":"mastra-ai/mastra","slug":"notification-summary-is-missing-resourceid","errorCode":null,"errorMessage":"Notification summary is missing resourceId","messagePattern":"Notification summary is missing resourceId","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/core/src/notifications/dispatcher.ts","lineNumber":195,"sourceCode":"    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      });\n  const target: SendAgentSignalOptions = allLowPriority","sourceCodeStart":177,"sourceCodeEnd":213,"githubUrl":"https://github.com/mastra-ai/mastra/blob/75dd419e613fe9c39f846ffc500716141b74fda6/packages/core/src/notifications/dispatcher.ts#L177-L213","documentation":"sendNotificationSummary builds an agent signal summarizing a batch of notification records, but it requires the first record to carry both agentId and resourceId. The resourceId identifies which resource (e.g. user/thread owner) the summary belongs to, and without it the signal cannot be routed. The library throws this error to fail fast on a malformed batch rather than dispatch an unattributable summary.","triggerScenarios":"Calling mastra.getNotifications() style flows or the notification dispatcher where records[0] lacks a resourceId field — typically when notifications were created without a resourceId or the record was manually constructed/persisted by custom storage.","commonSituations":"Custom storage implementations that drop resourceId when persisting; creating notifications programmatically via low-level storage APIs without setting resourceId; records migrated from older schema versions before resourceId was added.","solutions":["Ensure every NotificationRecord passed to sendNotificationSummary has a non-empty resourceId at creation time","Verify custom storage round-trips resourceId (serialize/deserialize preserves it)","Filter or repair records before batching: skip or backfill records missing resourceId","Check for schema migrations that may have dropped the resourceId column/field"],"exampleFix":"// before\nawait storage.createNotification({ threadId, agentId: 'agent-1', summary });\n// after\nawait storage.createNotification({ threadId, agentId: 'agent-1', resourceId: 'user-123', summary });","handlingStrategy":"validation","validationCode":"function canSummarize(records) {\n  const first = records[0];\n  return Boolean(first && first.agentId && first.resourceId);\n}\nif (!canSummarize(records)) throw new Error('Batch missing agentId or resourceId');","typeGuard":"function hasResourceId(r): r is NotificationRecord & { resourceId: string } {\n  return typeof r.resourceId === 'string' && r.resourceId.length > 0;\n}","tryCatchPattern":"try {\n  await sendNotificationSummary({ mastra, storage, records, now });\n} catch (e) {\n  if (e.message.includes('missing resourceId')) {\n    logger.error('Notification batch missing resourceId', { recordIds: records.map(r => r.id) });\n  }\n}","preventionTips":["Always set resourceId when creating notifications","Type records as NotificationRecord & { resourceId: string } at batch construction","Add a startup/storage test asserting resourceId survives persistence round-trips"],"tags":["notifications","missing-field","runtime"],"backgroundTag":"missing-required-identifier","analyzedSha":"75dd419e613fe9c39f846ffc500716141b74fda6","analyzedAt":"2026-08-30T00:15:31.844Z","schemaVersion":2},"datasetVersion":"2026-08-30T03:17:51.788Z"}