mastra-ai/mastra · error

Invalid audit event response

Error message

Invalid audit event response

What it means

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).

Source

Thrown at mastracode/factory-ui/src/ui/domains/factory/services/audit.ts:124

  baseUrl: string,
  factoryProjectId: string,
  options: { actions?: string[]; actorIds?: string[]; before?: string; limit?: number; signal?: AbortSignal } = {},
): Promise<AuditEventPage> {
  const query = new URLSearchParams();
  if (options.actions && options.actions.length > 0) query.set('actions', options.actions.join(','));
  if (options.actorIds && options.actorIds.length > 0) query.set('actorIds', options.actorIds.join(','));
  if (options.before) query.set('before', options.before);
  if (options.limit) query.set('limit', String(options.limit));
  const qs = query.size > 0 ? `?${query}` : '';
  const res = await fetch(`${baseUrl}/web/factory/projects/${encodeURIComponent(factoryProjectId)}/audit${qs}`, {
    headers: { Accept: 'application/json' },
    credentials: 'include',
    signal: options.signal,
  });
  if (!res.ok) return throwRequestError(res);

  const data: unknown = await res.json();
  if (!isAuditEventPage(data)) throw new Error('Invalid audit event response');
  return data;
}

export async function fetchAuditPortalLink(baseUrl: string): Promise<string | null> {
  const res = await fetch(`${baseUrl}/web/audit/portal-link`, {
    headers: { Accept: 'application/json' },
    credentials: 'include',
  });
  if (res.status === 404) return null;
  if (!res.ok) return throwRequestError(res);

  const data: unknown = await res.json();
  if (typeof data !== 'object' || data === null || !('url' in data) || typeof data.url !== 'string') {
    throw new Error('Invalid audit portal response');
  }
  return data.url;
}

View on GitHub (pinned to 75dd419e61)

Solutions

  1. Log the failing body and compare it against isAuditEventPage's expected fields; fix whichever side drifted.
  2. Redeploy frontend and API together so the audit-events contract matches.
  3. Check for a proxy/gateway rewriting or caching a stale 200 response.
  4. Wrap the call defensively if you tolerate bad pages (e.g. show an empty state instead of throwing).

Example fix

// before (server)
return Response.json(events);
// after (server)
return Response.json({ events, nextCursor, hasMore }); // match isAuditEventPage
Defensive patterns

Strategy: type-guard

Validate before calling

const res = await fetch(url);
if (!res.ok) throw new Error(`Audit events request failed: ${res.status}`);
const ct = res.headers.get('content-type') ?? '';
if (!ct.includes('application/json')) throw new Error('Audit endpoint returned non-JSON');

Type guard

function isAuditEventPage(v: unknown): v is AuditEventPage {
  if (typeof v !== 'object' || v === null || !Array.isArray((v as any).events)) return false;
  const o = v as Record<string, unknown>;
  return (o.nextCursor === undefined || typeof o.nextCursor === 'string') && typeof o.hasMore === 'boolean';
}

Try / catch

try {
  const page = await fetchAuditEvents(baseUrl, projectId);
} catch (e) {
  if (e instanceof Error && e.message === 'Invalid audit event response') {
    console.error('Audit API contract drift — inspect raw body');
    showEmptyState();
  }
}

Prevention

When it happens

Trigger: 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.

Common situations: 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).

Understand the failure class

Background: Schema validation failed / invalid input schema: payload rejected because its shape doesn't match the expected schema — this error's family across 28 libraries.

Related errors


AI-assisted analysis of mastra-ai/mastra@75dd419e61 (2026-08-30). Data as JSON: /api/errors/8c1db1841dc4f9b4. Report an issue: GitHub.