ChatGPTNextWeb/NextChat · error · Error
Client not found
Error message
Client ${clientId} not found What it means
Thrown at app/mcp/actions.ts:344 inside executeMcpAction when clientsMap.get(clientId) is undefined OR when the entry exists but client.client is null. Unlike pause/resume (which check the config file), this checks the live in-memory clientsMap, so a server can be in the config but still trigger this if it was never initialized, was paused, or failed to initialize (client set to null with an errorMsg).
Solutions
- Before executing, call getClientsStatus() and check the entry is 'active' (client present, no errorMsg).
- If the server is paused, call resumeMcpServer first and await it.
- If the server is in 'error' status, surface errorMsg to the user and offer re-add/resume rather than executing.
- Guard with a small retry/await loop keyed on initialization completion, or show a 'still initializing' message.
Example fix
// before
const result = await executeMcpAction(id, request);
// after
const status = (await getClientsStatus())[id];
if (!status || status.status !== "active") {
throw new Error(
`Client ${id} not ready (status: ${status?.status ?? "missing"}). ${status?.errorMsg ?? ""}`,
);
}
const result = await executeMcpAction(id, request); Defensive patterns
Strategy: validation
Validate before calling
import { getClientsStatus } from "@/app/mcp/actions";
async function isClientReady(clientId: string): Promise<boolean> {
const statuses = await getClientsStatus();
return statuses[clientId]?.status === "active";
}
if (await isClientReady(id)) {
const result = await executeMcpAction(id, request);
} else {
throw new Error(`Client ${id} is not ready; resume or re-add it.`);
} Type guard
type ClientStatus = { status: "active" | "paused" | "error" | "initializing"; errorMsg?: string };
function isActiveClient(
s: ClientStatus | undefined,
): s is ClientStatus & { status: "active" } {
return s?.status === "active";
} Try / catch
try {
return await executeMcpAction(id, request);
} catch (e) {
if (e instanceof Error && /not found/.test(e.message)) {
const status = (await getClientsStatus())[id];
throw new Error(
`Client ${id} not ready (status: ${status?.status ?? "missing"})`,
);
}
throw e;
} Prevention
- Never execute on a client whose status is not 'active'.
- Await initialization before exposing tools to the user.
- Auto-resume paused servers or hide their tools until resumed.
When it happens
Trigger: Calling executeMcpAction before initializeSingleClient has completed (async init still pending); calling it on a paused server (clientsMap entry was deleted on pause); calling it on a server whose createClient/listTools threw — the entry is {client:null, tools:null, errorMsg}; or on a completely unknown id.
Common situations: User invokes a tool immediately after adding a server before the async init resolves; server is in 'error' status; restartAllClients cleared the map; the worklet/client process crashed and clientsMap was not repopulated; calling execute on a paused server.
Related errors
- Failed to load tools
- Server not found
- Could not infer voiceLocale from voiceName!
- Failed to load preset servers
- Recording not started
AI-assisted analysis of ChatGPTNextWeb/NextChat@defdcdb55d (2026-08-12).
Data as JSON: /api/errors/d096398216e7036e.
Report an issue: GitHub.
Appendix: source
Thrown at app/mcp/actions.ts:344
for (const [clientId, serverConfig] of Object.entries(config.mcpServers)) {
await initializeSingleClient(clientId, serverConfig);
}
return config;
} catch (error) {
logger.error(`Failed to restart clients: ${error}`);
throw error;
}
}
// 执行 MCP 请求
export async function executeMcpAction(
clientId: string,
request: McpRequestMessage,
) {
try {
const client = clientsMap.get(clientId);
if (!client?.client) {
throw new Error(`Client ${clientId} not found`);
}
logger.info(`Executing request for [${clientId}]`);
return await executeRequest(client.client, request);
} catch (error) {
logger.error(`Failed to execute request for [${clientId}]: ${error}`);
throw error;
}
}
// 获取 MCP 配置文件
export async function getMcpConfigFromFile(): Promise<McpConfigData> {
try {
const configStr = await fs.readFile(CONFIG_PATH, "utf-8");
return JSON.parse(configStr);
} catch (error) {
logger.error(`Failed to load MCP config, using default config: ${error}`);
return DEFAULT_MCP_CONFIG;
}View on GitHub (pinned to defdcdb55d)