{"record":{"id":"8c1db1841dc4f9b4","repo":"mastra-ai/mastra","slug":"invalid-audit-event-response","errorCode":null,"errorMessage":"Invalid audit event response","messagePattern":"Invalid audit event response","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"mastracode/factory-ui/src/ui/domains/factory/services/audit.ts","lineNumber":124,"sourceCode":"  baseUrl: string,\n  factoryProjectId: string,\n  options: { actions?: string[]; actorIds?: string[]; before?: string; limit?: number; signal?: AbortSignal } = {},\n): Promise<AuditEventPage> {\n  const query = new URLSearchParams();\n  if (options.actions && options.actions.length > 0) query.set('actions', options.actions.join(','));\n  if (options.actorIds && options.actorIds.length > 0) query.set('actorIds', options.actorIds.join(','));\n  if (options.before) query.set('before', options.before);\n  if (options.limit) query.set('limit', String(options.limit));\n  const qs = query.size > 0 ? `?${query}` : '';\n  const res = await fetch(`${baseUrl}/web/factory/projects/${encodeURIComponent(factoryProjectId)}/audit${qs}`, {\n    headers: { Accept: 'application/json' },\n    credentials: 'include',\n    signal: options.signal,\n  });\n  if (!res.ok) return throwRequestError(res);\n\n  const data: unknown = await res.json();\n  if (!isAuditEventPage(data)) throw new Error('Invalid audit event response');\n  return data;\n}\n\nexport async function fetchAuditPortalLink(baseUrl: string): Promise<string | null> {\n  const res = await fetch(`${baseUrl}/web/audit/portal-link`, {\n    headers: { Accept: 'application/json' },\n    credentials: 'include',\n  });\n  if (res.status === 404) return null;\n  if (!res.ok) return throwRequestError(res);\n\n  const data: unknown = await res.json();\n  if (typeof data !== 'object' || data === null || !('url' in data) || typeof data.url !== 'string') {\n    throw new Error('Invalid audit portal response');\n  }\n  return data.url;\n}\n","sourceCodeStart":106,"sourceCodeEnd":142,"githubUrl":"https://github.com/mastra-ai/mastra/blob/75dd419e613fe9c39f846ffc500716141b74fda6/mastracode/factory-ui/src/ui/domains/factory/services/audit.ts#L106-L142","documentation":"fetchAuditEvents validates the JSON body of a 200 response against the isAuditEventPage shape guard; if it doesn't match the expected audit-event page structure, it throws 'Invalid audit event response'. This protects callers from trusting a malformed payload (page metadata, event array, cursors).","triggerScenarios":"GET audit events returns 200 with JSON that fails isAuditEventPage — wrong/missing fields, events not an array, fields with wrong types — from an API version mismatch, a proxy substituting content, or a handler bug.","commonSituations":"Backend deployed with a changed audit-events schema while the frontend expects the old shape; CDN/HTML fallback served with 200; handler returning unwrapped data ({events} missing pagination fields).","solutions":["Log the failing body and compare it against isAuditEventPage's expected fields; fix whichever side drifted.","Redeploy frontend and API together so the audit-events contract matches.","Check for a proxy/gateway rewriting or caching a stale 200 response.","Wrap the call defensively if you tolerate bad pages (e.g. show an empty state instead of throwing)."],"exampleFix":"// before (server)\nreturn Response.json(events);\n// after (server)\nreturn Response.json({ events, nextCursor, hasMore }); // match isAuditEventPage","handlingStrategy":"type-guard","validationCode":"const res = await fetch(url);\nif (!res.ok) throw new Error(`Audit events request failed: ${res.status}`);\nconst ct = res.headers.get('content-type') ?? '';\nif (!ct.includes('application/json')) throw new Error('Audit endpoint returned non-JSON');","typeGuard":"function isAuditEventPage(v: unknown): v is AuditEventPage {\n  if (typeof v !== 'object' || v === null || !Array.isArray((v as any).events)) return false;\n  const o = v as Record<string, unknown>;\n  return (o.nextCursor === undefined || typeof o.nextCursor === 'string') && typeof o.hasMore === 'boolean';\n}","tryCatchPattern":"try {\n  const page = await fetchAuditEvents(baseUrl, projectId);\n} catch (e) {\n  if (e instanceof Error && e.message === 'Invalid audit event response') {\n    console.error('Audit API contract drift — inspect raw body');\n    showEmptyState();\n  }\n}","preventionTips":["Share the AuditEventPage type/schema between client and server.","Snapshot-test the audit events endpoint response shape.","Deploy frontend and API schema changes atomically.","Guard against proxies serving stale cached 200s."],"tags":["schema-validation","http","audit","api-contract"],"backgroundTag":"schema-validation-failed","analyzedSha":"75dd419e613fe9c39f846ffc500716141b74fda6","analyzedAt":"2026-08-30T00:15:31.844Z","schemaVersion":2},"datasetVersion":"2026-08-30T03:17:51.788Z"}