{"record":{"id":"81414a6f687a580a","repo":"koala73/worldmonitor","slug":"no-primary-military-feeds-are-available","errorCode":null,"errorMessage":"No primary military feeds are available","messagePattern":"No primary military feeds are available","errorType":"exception","errorClass":"McpSourceUnavailableError","httpStatus":null,"severity":"error","filePath":"api/mcp/registry/analysis-tools.ts","lineNumber":179,"sourceCode":"    items: { type: 'string' },\n    description: 'Required cache keys that were missing or unreadable; their contribution is not treated as quiet.',\n  },\n  failed_inputs: {\n    type: 'array',\n    items: { type: 'string' },\n    description: 'Subset of unavailable_inputs whose Redis read failed rather than returning a genuine miss.',\n  },\n} as const;\n\ntype AnalysisFreshness = Awaited<ReturnType<typeof readCachesWithFreshness>>['freshness'];\n\nfunction requireAnyInput(\n  payloads: unknown[],\n  freshness: AnalysisFreshness,\n  message: string,\n): void {\n  if (payloads.every((value) => value === null)) {\n    throw new McpSourceUnavailableError(\n      message,\n      freshness.unavailable_inputs,\n      freshness.failed_inputs,\n    );\n  }\n}\n\nfunction resolveLimit(raw: unknown, fallback: number): number {\n  if (raw === undefined || raw === null) return fallback;\n  const parsed = Math.round(Number(raw));\n  if (!Number.isFinite(parsed)) return fallback;\n  if (parsed <= 0) return Number.POSITIVE_INFINITY;\n  return parsed;\n}\n\n// Keep the analysis schemas aligned with cacheEnvelope(). Content age is not a\n// universal rule: evaluateFreshness() applies it only to checks that explicitly\n// declare honorContentAge.","sourceCodeStart":161,"sourceCodeEnd":197,"githubUrl":"https://github.com/koala73/worldmonitor/blob/9361220cc013571781071f0206e4d80fd14b2f7f/api/mcp/registry/analysis-tools.ts#L161-L197","documentation":"McpSourceUnavailableError thrown by requireAnyInput (api/mcp/registry/analysis-tools.ts:173) for tools needing primary military feeds: every military input cache payload was null. The typed fields carry unavailable_inputs and failed_inputs so the caller can tell a genuine data gap (feeds never seeded / expired) from Redis read failures.","triggerScenarios":"Calling a military analysis tool while all military feed cache keys (e.g. conflict/military event stores) read null — military producer pipelines down, keys evicted/expired, environment never seeded, or Upstash read errors.","commonSituations":"Military data producer job failed upstream. Deployment ordering issue where MCP ships before seeds. Upstash outage window. Eviction policy pressure removing the military keys.","solutions":["Distinguish miss vs failure via error.data (unavailable_inputs vs failed_inputs)","Confirm military feed keys are fresh on the health endpoint","Retry during/after the next military producer cycle","Seed the military keys or restart their producer if health shows them absent"],"exampleFix":null,"handlingStrategy":"retry","validationCode":"const health = await fetch('https://<host>/api/health').then(r => r.json());\nconst militaryReady = militaryFeedKeys.every(k => health?.datasets?.[k]?.fresh !== false);","typeGuard":"function isSourceUnavailable(e) {\n  return e?.name === 'McpSourceUnavailableError' || Array.isArray(e?.data?.unavailable_inputs);\n}","tryCatchPattern":"try {\n  await client.callTool(militaryAnalysisTool, args);\n} catch (e) {\n  if (isSourceUnavailable(e) && e.data?.failed_inputs?.length) return retryWithBackoff(call, 3);\n  if (isSourceUnavailable(e)) return retryAfter(producerCadence);\n  throw e;\n}","preventionTips":["Watch military feed keys on the health surface before running dependent analyses","Escalate persistent unavailable_inputs to the military data producer owner","Cache last-good analysis output client-side to degrade gracefully during feed gaps"],"tags":["mcp","analysis","redis","data-source","military"],"backgroundTag":"data-source-unavailable","analyzedSha":"9361220cc013571781071f0206e4d80fd14b2f7f","analyzedAt":"2026-08-21T16:51:25.751Z","contentChangedAt":"2026-08-21T16:51:25.751Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}