koala73/worldmonitor · error · McpSourceUnavailableError
Feed digest unavailable for
Error message
Feed digest unavailable for ${variant}/en What it means
McpSourceUnavailableError thrown in list-feed-digest (api/mcp/registry/nlp-tools.ts:195): the digest endpoint fetch itself succeeded (assertToolFetchOk passed) but the response body has zero categories AND zero feedStatuses — structurally an empty shell. The error names the exact cache key ('news:digest:v1:<variant>:en') in unavailable_inputs so the caller knows which producer is missing, with an empty failed_inputs list (this is a genuine miss, not a Redis read error).
Solutions
- Retry later — the digest producer runs on a cadence and the shell becomes populated after the next run
- Check /api/health for the news:digest:v1:<variant>:en key freshness
- Hit the underlying digest API endpoint directly to confirm it also returns the empty shell (isolates MCP from the producer)
- If the variant should already be live, run the news digest seed script or restart its producer
Defensive patterns
Strategy: retry
Validate before calling
// Pre-check the digest key for the variant via health before calling
const health = await fetch('https://<host>/api/health').then(r => r.json());
if (!health?.datasets?.[`news:digest:v1:${variant}:en`]?.fresh) defer('digest not published yet'); Type guard
function isSourceUnavailable(e) {
return e?.name === 'McpSourceUnavailableError'
|| (typeof e?.message === 'string' && e.message.startsWith('Feed digest unavailable for '));
} Try / catch
try {
return await client.callTool('list-feed-digest', { variant });
} catch (e) {
if (isSourceUnavailable(e)) {
// unavailable_inputs names the exact key: news:digest:v1:<variant>:en
return retryAfter(digestProducerCadenceMs);
}
throw e;
} Prevention
- For a newly deployed variant, verify its digest producer ran once before advertising the tool
- The error names the exact cache key — log it and alert per-variant rather than globally
- Distinguish this from an HTTP failure: the endpoint returned 200; only the content was empty
When it happens
Trigger: Calling list-feed-digest for a variant whose digest producer has not published yet — e.g. a newly rolled-out language variant where the news digest cron has not run, or a deployment where the digest seed was skipped. The endpoint returns 200 with an empty object.
Common situations: New variant deployment ordering (MCP tool shipped before the variant's digest producer). Digest generation job failed silently upstream. Digest key evicted/TTL-expired so the endpoint serves an empty shell. Local dev against prod API without the local digest seeded.
Related errors
- No digest input feeds are available
- No convergence input feeds are available
- No event feeds are available for exposure enrichment
- No focal-point input feeds are available
- No hotspot-escalation input feeds are available
AI-assisted analysis of koala73/worldmonitor@e586b8b4b8 (2026-08-27).
Data as JSON: /api/errors/81c552757e785cd3.
Report an issue: GitHub.
Appendix: source
Thrown at api/mcp/registry/nlp-tools.ts:321
.slice(0, 12),
stale: cov.state === 'stale',
// #7084: complete the stale disclosure. The flag alone says the
// evidence is old without saying how old or why — an agent deciding
// whether stale evidence is usable needs the age.
staleAgeSeconds: cov.state === 'stale'
? nlpClampInt(cov.staleAgeSeconds ?? 0, 0, Number.MAX_SAFE_INTEGER, 0)
: 0,
staleReason: cov.state === 'stale'
? nlpTruncateUtf8(cov.staleReason ?? '', NLP_DIGEST_METADATA_MAX_BYTES)
: '',
}
: undefined;
if (
Object.keys(categories).length === 0
&& Object.keys(body.feedStatuses ?? {}).length === 0
&& digestCoverage?.state !== 'unavailable'
) {
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 if (digestCoverage?.state === 'unavailable') {
// This is a valid empty response, not an unknown category. Preserve the
// requested filter and the explicit coverage state without a misleading
// corrective note.
groups = [];View on GitHub (pinned to e586b8b4b8)