{"record":{"id":"0f7b1ae1c223971a","repo":"yikart/AiToEarn","slug":"mcp-server-not-found-for-session","errorCode":null,"errorMessage":"MCP server not found for session","messagePattern":"MCP server not found for session","errorType":"http","errorClass":null,"httpStatus":404,"severity":"error","filePath":"project/aitoearn-backend/libs/nest-mcp/src/services/mcp-sse.service.ts","lineNumber":115,"sourceCode":"  }\n\n  /**\n   * Handle message processing for SSE\n   */\n  async handleMessage(rawReq: any, rawRes: any, body: unknown): Promise<any> {\n    const adapter = HttpAdapterFactory.getAdapter(rawReq, rawRes)\n    const req = adapter.adaptRequest(rawReq)\n    const res = adapter.adaptResponse(rawRes)\n    const sessionId = req.query['sessionId'] as string\n    const transport = this.transports.get(sessionId)\n\n    if (!transport) {\n      return res.status(404).send('Session not found')\n    }\n\n    const mcpServer = this.mcpServers.get(sessionId)\n    if (!mcpServer) {\n      return res.status(404).send('MCP server not found for session')\n    }\n\n    // Resolve the request-scoped tool executor service\n    const contextId = ContextIdFactory.getByRequest(req)\n    const executor = await this.moduleRef.resolve(\n      McpExecutorService,\n      contextId,\n    )\n\n    // Register request handlers with the user context from this specific request\n    executor.registerRequestHandlers(mcpServer, req)\n\n    // Process the message\n    await transport.handlePostMessage(req.raw, res.raw, body)\n  }\n}\n","sourceCodeStart":97,"sourceCodeEnd":132,"githubUrl":"https://github.com/yikart/AiToEarn/blob/d3aa8bea5b146a8675607cf0144d891aad3e9683/project/aitoearn-backend/libs/nest-mcp/src/services/mcp-sse.service.ts#L97-L132","documentation":"After finding a transport, the SSE service looks up the associated McpServer instance for the same sessionId. If the transport exists but no server is registered, it responds 404 'MCP server not found for session'. This indicates partial internal state: the two maps (transports and mcpServers) are out of sync.","triggerScenarios":"POST with a sessionId present in transports but absent from mcpServers — e.g., server registration failed or was cleaned up while the transport leaked, or state maps updated non-atomically during connect/disconnect.","commonSituations":"Error during session initialization after transport creation; cleanup routine removing mcpServers entries but not transports; race between concurrent connect/disconnect; hot reload or partial restart clearing one map.","solutions":["Close and re-open the SSE session so transport and server are created together","Make transport and McpServer registration/disposal atomic (register both together, clean both together on failure)","Add cleanup on transport close to remove both entries, preventing stale transports","Log and correlate sessionId lifecycle events to find where the maps diverge"],"exampleFix":"// before\nthis.transports.set(sessionId, transport)\nawait this.startServer(sessionId) // if this throws, transport leaks\n// after\ntry {\n  const server = await this.startServer(sessionId)\n  this.transports.set(sessionId, transport)\n  this.mcpServers.set(sessionId, server)\n} catch (e) {\n  transport.close()\n  throw e\n}","handlingStrategy":"fallback","validationCode":"// client cannot detect this in advance; on the server, guard lifecycle:\n// assert(this.mcpServers.has(sessionId), 'server must be registered with transport')","typeGuard":"function isServerMissing(resp: { status: number; body?: unknown }): boolean {\n  return resp.status === 404 && typeof resp.body === 'string' && resp.body.includes('MCP server not found')\n}","tryCatchPattern":"try {\n  await postMessage(sessionId, payload)\n} catch (e) {\n  if (isServerMissingErr(e)) {\n    await forceCloseSession(sessionId)   // clears desynced transport\n    sessionId = await reconnectSse()\n    await postMessage(sessionId, payload)\n  } else { throw e }\n}","preventionTips":["Register transport and McpServer atomically in one code path","Dispose both maps together when a connection closes","Fail fast if server init errors instead of leaving a transport behind","Add invariant logging for sessionId presence across both maps"],"tags":["mcp","sse","session","state-management"],"backgroundTag":"mcp-session-not-found","analyzedSha":"d3aa8bea5b146a8675607cf0144d891aad3e9683","analyzedAt":"2026-08-31T14:19:24.185Z","schemaVersion":2},"datasetVersion":"2026-08-31T19:17:28.585Z"}