mastra-ai/mastra · error

Attention read-all response is missing its continuation curs

Error message

Attention read-all response is missing its continuation cursor.

What it means

markAllFactoryAttentionRead pages through the attention read-all endpoint; when a page reports hasMore=true but omits nextCursor, the continuation cursor needed to keep marking items read is missing, so it throws rather than silently stopping or looping forever. This is a strict server-response invariant check against inconsistent API responses.

Source

Thrown at mastracode/factory-ui/src/ui/domains/factory/services/attention.ts:111

  item: FactoryAttentionItem,
  action: FactoryAttentionReceiptAction,
): Promise<{ receipt: { key: string; state: 'read' | 'archived'; readAt: string; archivedAt: string | null } }> {
  return requestJson(
    `${baseUrl}/web/factory/projects/${encodeURIComponent(factoryProjectId)}/attention/${item.kind}/${encodeURIComponent(attentionItemSourceId(item))}/${item.occurrence}/${action}`,
    { method: 'POST' },
  );
}

export async function markAllFactoryAttentionRead(baseUrl: string, factoryProjectId: string): Promise<{ ok: true }> {
  let before: string | undefined;
  while (true) {
    const query = before ? `?before=${encodeURIComponent(before)}` : '';
    const page = await requestJson<{ ok: true; hasMore: boolean; nextCursor?: string }>(
      `${baseUrl}/web/factory/projects/${encodeURIComponent(factoryProjectId)}/attention/read-all${query}`,
      { method: 'POST' },
    );
    if (!page.hasMore) return { ok: true };
    if (!page.nextCursor) throw new Error('Attention read-all response is missing its continuation cursor.');
    before = page.nextCursor;
  }
}

View on GitHub (pinned to 75dd419e61)

Solutions

  1. Inspect the raw read-all response confirming hasMore=true with no nextCursor; report/fix the server to always return nextCursor when hasMore is true.
  2. Align frontend and API versions (deploy both together).
  3. Check for any response-transforming middleware/proxy stripping fields.
  4. As a caller, catch and surface the error; re-running markAll after the server fix resumes marking items read.

Example fix

// before (server)
return Response.json({ ok: true, hasMore: true });
// after (server)
return Response.json({ ok: true, hasMore: true, nextCursor: oldestUnreadTs });
Defensive patterns

Strategy: try-catch

Validate before calling

if (page.hasMore && typeof page.nextCursor !== 'string') throw new Error('Response cursor invariant violated');

Type guard

function isReadAllPage(v: unknown): v is { ok: true; hasMore: boolean; nextCursor?: string } {
  if (typeof v !== 'object' || v === null) return false;
  const o = v as Record<string, unknown>;
  return o.ok === true && typeof o.hasMore === 'boolean' && (o.nextCursor === undefined || typeof o.nextCursor === 'string');
}

Try / catch

try {
  await markAllFactoryAttentionRead(baseUrl, projectId);
} catch (e) {
  showToast('Could not mark all as read; some items may remain unread.');
  console.error(e); // invariant violation: server sent hasMore without nextCursor
}

Prevention

When it happens

Trigger: POST /web/factory/projects/:id/attention/read-all?before=... returns { ok:true, hasMore:true } without nextCursor — server bug, version skew between client expectations and API, or a proxy altering the response body.

Common situations: API deployed with pagination behavior changed while the frontend expects the old contract; gateway/response transform stripping optional fields; partial rollout where the endpoint version differs from the client.

Related errors


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