{"record":{"id":"4c9bfa775807f2a6","repo":"different-ai/openwork","slug":"mcp-lifecycle-deadline-4c9bfa","errorCode":"MCP_LIFECYCLE_DEADLINE","errorMessage":"MCP_LIFECYCLE_DEADLINE","messagePattern":"MCP_LIFECYCLE_DEADLINE","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"error","filePath":"ee/apps/den-api/src/capability-sources/external-mcp-diagnostics.ts","lineNumber":1179,"sourceCode":"      phase: \"NETWORK_TCP\",\n      category: \"network_failure\",\n      code: `MCP_${normalizedCode}`,\n      retryable: normalizedCode === \"ECONNRESET\" || normalizedCode === \"ETIMEDOUT\" || normalizedCode.startsWith(\"UND_ERR_\"),\n      actionOwner: \"network_admin\",\n      operatorAction: \"Verify provider allowlists, firewall rules, proxy requirements, and service availability from Den.\",\n    }\n  }\n  return null\n}\n\nfunction classifyError(error: unknown, fallbackPhase: ExternalMcpDiagnosticPhase): Classification {\n  // The enterprise client aborts with a RequestTimeout MCP error of its own, so\n  // check for our marker before any JSON-RPC code is read as the provider's.\n  if (error instanceof ExternalMcpLifecycleDeadlineError || isEnterpriseMcpLifecycleDeadline(error)) {\n    return {\n      phase: fallbackPhase,\n      category: \"lifecycle_deadline\",\n      code: \"MCP_LIFECYCLE_DEADLINE\",\n      retryable: true,\n      actionOwner: \"provider_admin\",\n      operatorAction: fallbackPhase === \"MCP_TOOL_EXECUTION\"\n        ? \"Retry the capability, and reduce provider latency for this tool if it keeps running past the bounded deadline.\"\n        : \"Reduce provider latency or catalog pagination so the complete MCP lifecycle finishes within the bounded deadline, then retry.\",\n    }\n  }\n  if (error instanceof ExternalMcpResponseBodyLimitError) {\n    return {\n      phase: fallbackPhase,\n      category: \"response_too_large\",\n      code: \"MCP_RESPONSE_BODY_LIMIT\",\n      retryable: false,\n      actionOwner: \"provider_admin\",\n      operatorAction: \"Reduce the provider response size, tool catalog, or event-stream payload before retrying.\",\n    }\n  }\n  if (error instanceof PrivateUrlError) {","sourceCodeStart":1161,"sourceCodeEnd":1197,"githubUrl":"https://github.com/different-ai/openwork/blob/2b7df46e8ae1517d64c896c7793d2d52ec845669/ee/apps/den-api/src/capability-sources/external-mcp-diagnostics.ts#L1161-L1197","documentation":"MCP_LIFECYCLE_DEADLINE is returned when the operation failed because ExternalMcpLifecycleDeadlineError fired — the gateway's bounded lifecycle deadline expired — or because the enterprise client raised its own matching RequestTimeout. The marker check runs before JSON-RPC error codes are read so the client's own timeout is not misattributed to the provider. It is retryable and owned by the provider admin: the provider was too slow to complete the full MCP lifecycle (or one tool call) within the deadline.","triggerScenarios":"A tool execution (MCP_TOOL_EXECUTION) or a full catalog lifecycle (initialize + discovery) exceeding the bounded deadline, throwing ExternalMcpLifecycleDeadlineError; or an enterprise MCP client timeout (RequestTimeout) being detected via isEnterpriseMcpLifecycleDeadline.","commonSituations":"Slow provider backends or models making individual tool calls run for minutes; enormous tool catalogs requiring paginated discovery that exceeds the lifecycle budget; provider outages or degraded performance; network latency between gateway and provider.","solutions":["Retry the capability; transient provider slowness often clears on a subsequent attempt.","If tool execution repeatedly exceeds the deadline, reduce provider latency for that tool (faster backend, smaller inputs, async job pattern).","For discovery-phase deadlines, reduce catalog size or pagination so the complete MCP lifecycle finishes within the bounded deadline."],"exampleFix":"// before: one giant unpaginated discovery that blows the deadline\nconst tools = await listAllTools(client) // thousands of tools\n// after: bounded pagination\nconst tools = []\nfor await (const page of listToolsPaginated(client, { pageSize: 50 })) tools.push(...page)","handlingStrategy":"retry","validationCode":"// estimate catalog size before a full lifecycle to avoid deadline blowouts\nconst probe = await client.listTools({ pageSize: 1 })\nif (probe.totalCount > 500) {\n  throw new Error(`catalog too large (${probe.totalCount}) for single lifecycle deadline; paginate`)\n}","typeGuard":"function isLifecycleDeadline(d: { code: string }): boolean {\n  return d.code === 'MCP_LIFECYCLE_DEADLINE'\n}","tryCatchPattern":"try {\n  return await runCapability(req)\n} catch (e) {\n  if (isLifecycleDeadline(e.diagnostic)) {\n    // retryable: retry once; if it recurs, reduce provider latency or paginate discovery\n    return await runCapability(req)\n  }\n  throw e\n}","preventionTips":["Set tool-call timeouts on the provider side below the gateway's lifecycle deadline.","Paginate large tool catalogs instead of listing everything in one lifecycle.","Monitor provider p99 latency and alert before it approaches the bounded deadline."],"tags":["mcp","timeout","deadline","latency","retryable"],"backgroundTag":"request-timeout","analyzedSha":"2b7df46e8ae1517d64c896c7793d2d52ec845669","analyzedAt":"2026-09-01T07:59:23.713Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}