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 > 0View on GitHub (pinned to ffec79ac33)
Solutions
- Verify the digest exists in Redis: check GET /api/health for the news:digest:v1:<variant>:en coverage and the corresponding seed-meta key.
- 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.
- Confirm the variant parameter matches a variant the digest producer actually runs for (full/en is the primary; tech/en, energy/en may lag).
- 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
- Confirm the NLP digest worker is running for the requested variant before relying on no-text mode.
- Provide explicit text to extract_entities as a fallback when the digest is unavailable.
- Monitor the news:digest:v1:*:en keys in GET /api/health coverage.
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
- No event feeds are available for exposure enrichment
- No digest input feeds are available
- No hotspot-escalation input feeds are available
- The submarine-cable catalog is unavailable
- Seeded world brief unavailable (${result.reason})
AI-assisted analysis of koala73/worldmonitor@ffec79ac33 (2026-08-12).
Data as JSON: /api/errors/81c552757e785cd3.
Report an issue: GitHub.