FlowiseAI/Flowise · error · Error

Client is not initialized

Error message

Client is not initialized

What it means

Thrown inside the per-tool mapping loop in MCPToolkit.get_tools when this.client === null at the moment a tool wrapper is constructed. Because initialize() calls this.client.close() right after get_tools() returns, async scheduling can let the close (and any code that nulls the client) interleave with the still-running map callbacks, tripping the inner guard.

Source

Thrown at packages/components/nodes/tools/MCP/core.ts:190

        if (this._tools === null) {
            this.client = await this.createClient()

            this._tools = await this.client.request({ method: 'tools/list' }, ListToolsResultSchema)

            this.tools = await this.get_tools()

            // Close the initial client after initialization
            await this.client.close()
        }
    }

    async get_tools(): Promise<Tool[]> {
        if (this._tools === null || this.client === null) {
            throw new Error('Must initialize the toolkit first')
        }
        const toolsPromises = this._tools.tools.map(async (tool: any) => {
            if (this.client === null) {
                throw new Error('Client is not initialized')
            }
            const argsSchema = tool.inputSchema ?? { type: 'object', properties: {} }
            const safeName = sanitizeMCPToolName(tool.name)
            const safeDescription = sanitizeMCPToolDescription(tool.description || tool.name)
            return await MCPTool({
                toolkit: this,
                name: safeName,
                description: safeDescription,
                argsSchema
            })
        })
        const res = await Promise.allSettled(toolsPromises)
        const errors = res.filter((r) => r.status === 'rejected')
        if (errors.length !== 0) {
            console.error('MCP Tools failed to be resolved', errors)
        }
        const successes = res.filter((r) => r.status === 'fulfilled').map((r) => r.value)
        return successes

View on GitHub (pinned to abe4a8601a)

Solutions

  1. Do not call initialize() (or close()) on the same MCPToolkit concurrently; serialize lifecycle calls.
  2. Avoid re-invoking get_tools() manually; use the cached toolkit.tools populated by initialize().
  3. Use one MCPToolkit instance per session and never null its client externally.

Example fix

// before
await toolkit.initialize()      // closes client after get_tools
await toolkit.get_tools()       // client now null -> throws

// after
await toolkit.initialize()
const tools = toolkit.tools ?? [] // use cached result
Defensive patterns

Strategy: try-catch

Validate before calling

// Ensure no concurrent close()/initialize() runs while get_tools() executes.
if (toolkit.client === null) {
  throw new Error('Refusing to call get_tools(): client was closed; create a new MCPToolkit')
}

Type guard

const hasLiveClient = (tk: MCPToolkit): boolean => tk.client !== null

Try / catch

try {
  return await toolkit.get_tools()
} catch (e) {
  if (e.message === 'Client is not initialized') {
    // build a fresh toolkit and re-initialize instead of retrying on the closed one
  }
  throw e
}

Prevention

When it happens

Trigger: get_tools() is running its Promise.allSettled over tools while this.client becomes null concurrently — e.g. initialize() proceeding to client.close() or external code nulling the client mid-loop. Each tool's MCPTool() closure captures this and re-checks this.client before building the tool.

Common situations: Concurrent initialize()/close() calls on the same toolkit; a second initialize() racing the first; tool construction deferred across an await while the original client is torn down.

Related errors


AI-assisted analysis of FlowiseAI/Flowise@abe4a8601a (2026-08-12). Data as JSON: /api/errors/f779f60397da4915. Report an issue: GitHub.