koala73/worldmonitor · error · McpSourceUnavailableError

Feed digest unavailable for ${variant}/en

Error message

Feed digest unavailable for ${variant}/en

What it means

Thrown by fetchNlpDigestItems() (shared by the extract_entities no-text mode and get_news_clusters tools) when the list-feed-digest endpoint returned HTTP 200 but the body contains zero category keys AND zero feedStatus keys — meaning the news digest has not been seeded for the requested variant/language combination. The cache key news:digest:v1:<variant>:en is reported as unavailable with no failed_inputs (this is a genuine miss, not a Redis read failure).

Source

Thrown at api/mcp/registry/nlp-tools.ts:192

): Promise<NlpDigestFetch> {
  const digestUrl = `${base}/api/news/v1/list-feed-digest?variant=${variant}&lang=en`;
  const auth = await buildAuthHeaders(context, 'GET', digestUrl, null);
  const res = await fetch(digestUrl, {
    headers: { ...auth, 'User-Agent': NLP_UA },
    signal: AbortSignal.timeout(NLP_DIGEST_TIMEOUT_MS),
  });
  assertToolFetchOk(res, 'list-feed-digest');
  const body = await res.json() as {
    categories?: Record<string, NlpDigestCategoryGroup>;
    feedStatuses?: Record<string, string>;
    generatedAt?: string;
  };

  const seen = new Set<string>();
  const items: NewsItemCore[] = [];
  const categories = body.categories ?? {};
  if (Object.keys(categories).length === 0 && Object.keys(body.feedStatuses ?? {}).length === 0) {
    throw new McpSourceUnavailableError(
      `Feed digest unavailable for ${variant}/en`,
      [`news:digest:v1:${variant}:en`],
      [],
    );
  }
  const availableCategories = Object.keys(categories).sort();
  let groups: NlpDigestCategoryGroup[];
  let note: string | undefined;

  if (!category) {
    groups = Object.values(categories);
  } else if (Object.prototype.hasOwnProperty.call(categories, category)) {
    groups = [categories[category]!];
  } else {
    groups = [];
    // Prefer the live snapshot keys so agents see what this cycle actually
    // carries; fall back to the static enum when the digest is empty.
    const listed = availableCategories.length > 0

View on GitHub (pinned to ffec79ac33)

Solutions

  1. Verify the digest exists in Redis: check GET /api/health for the news:digest:v1:<variant>:en coverage and the corresponding seed-meta key.
  2. Retry after the NLP digest cadence (~15 minutes) — the digest is produced on a recurring cycle and a transient gap resolves on the next run.
  3. Confirm the variant parameter matches a variant the digest producer actually runs for (full/en is the primary; tech/en, energy/en may lag).
  4. If persistent, check that the NLP digest worker (Railway) is running and writing the expected Redis keys.
Defensive patterns

Strategy: fallback

Validate before calling

// Before calling extract_entities/get_news_clusters, verify the digest is seeded
const health = await fetch('/api/health').then(r => r.json());
const digestKey = `news:digest:v1:${variant}:en`;
if (!health.coverage?.[digestKey]) {
  throw new Error(`Digest not seeded for ${variant}/en; retry after NLP cycle`);
}

Type guard

function isMcpSourceUnavailableError(e: unknown): e is { unavailableInputs: string[]; failedInputs: string[] } & Error {
  return e instanceof Error && (e as any).name === 'McpSourceUnavailableError';
}

Try / catch

try {
  const entities = await callMcpTool('extract_entities', { text: '...' });
} catch (e) {
  if (isMcpSourceUnavailableError(e) && e.unavailableInputs.includes(`news:digest:v1:${variant}:en`)) {
    // Digest not ready — fall back to explicit text mode or retry
    const fallback = await callMcpTool('extract_entities', { text: providedText });
  } else throw e;
}

Prevention

When it happens

Trigger: Calling extract_entities or get_news_clusters with a variant whose digest is not yet seeded (e.g. 'tech' variant when only 'full' has been produced); calling right after a deploy before the NLP digest pipeline runs; requesting a variant that has no RSS sources configured. The endpoint returned 200 (it passed assertToolFetchOk) but with an empty payload.

Common situations: Variant mismatch — the MCP dispatch defaults to one variant but the digest producer ran for a different one; a digest pipeline outage where the NLP worker is down so no fresh digests are written; a Redis key naming drift where the producer writes a different key suffix than the reader expects.

Related errors


AI-assisted analysis of koala73/worldmonitor@ffec79ac33 (2026-08-12). Data as JSON: /api/errors/81c552757e785cd3. Report an issue: GitHub.