April 27, 2026 โ€ข Version: 2026.3.2

MCP Plugin Initialize Timeout: Hardcoded 30s Causes Handshake Failure

The MCP initialization handshake timeout is hardcoded at 30,000ms with no configuration override, causing plugins with heavy startup dependencies to fail despite successful retry.

๐Ÿ” Symptoms

Primary Error Manifestation

The MCP plugin initialization handshake times out unconditionally after 30 seconds, producing the following error stream:

Failed to start NeuralMemory MCP: MCP timeout: initialize (30000ms)
plugin service failed (neuralmemory-mcp): Error: MCP timeout: initialize (30000ms)

Environment Context

  • Affected Plugin: @neuralmemory/openclaw-plugin 1.4.1
  • OpenClaw Version: 2026.3.2
  • Node.js: v24.14.0
  • OS: Ubuntu 6.17.0 (GCP)

Workaround Behavior

The plugin subsequently initializes successfully on retry after 10โ€“15 seconds:

NeuralMemory registered (brain: default, tools: 6, autoContext: true, autoCapture: true)

Ineffective Configuration Attempts

The following configuration options do not affect the MCP init timeout:

  • plugins.entries.neuralmemory.config.timeout: 90000 โ€” applies only to post-init MCP requests
  • startupTimeoutMs: 120000 โ€” applies to plugin startup lifecycle, not MCP client layer

Diagnostic Command

To confirm the timeout occurs during MCP initialization:

openclaw debug --plugin neuralmemory 2>&1 | grep -E "(timeout|initialize|MCP)"

Expected output during failure:

[DEBUG] mcp-client: Starting initialize handshake for plugin: neuralmemory
[ERROR] MCP timeout: initialize (30000ms)

๐Ÿง  Root Cause

Architectural Layer Mismatch

The timeout behavior originates from a layer separation between plugin lifecycle management and the MCP client protocol layer.

Layer 1: Plugin Lifecycle Manager

The plugin service manager reads startupTimeoutMs from configuration:

js // In plugin-service.js or equivalent const startupTimeoutMs = Math.min(12e4, Math.max(1e0, opts.startupTimeoutMs ?? accountInfo.config.startupTimeoutMs ?? 3e4));

This timeout governs the overall plugin startup sequence but is isolated from MCP protocol operations.

Layer 2: MCP Client Handshake

The MCP client layer implements a separate, hardcoded timeout for the initialize handshake:

js // In mcp-client.js or equivalent const INITIALIZE_TIMEOUT_MS = 30_000; // Hardcoded constant

const initializePromise = mcpConnection.initialize(); const timeoutPromise = new Promise((_, reject) => { setTimeout(() => reject(new Error(MCP timeout: initialize (${INITIALIZE_TIMEOUT_MS}ms))), INITIALIZE_TIMEOUT_MS); });

await Promise.race([initializePromise, timeoutPromise]);

Failure Sequence

  1. Plugin service spawns MCP client connection
  2. MCP client initiates initialize handshake with server
  3. Server begins heavy initialization (e.g., loading ML models like sentence-transformers)
  4. 30-second mark is reached before server completes initialization
  5. MCP client rejects with timeout error
  6. Plugin service marks plugin as failed and retries
  7. On retry, model is cached and initialization completes within 10โ€“15 seconds

Configuration Propagation Gap

The configuration hierarchy does not propagate to the MCP client layer:

plugins.entries..config.timeout โ†’ MCP request layer (post-init) โœ“ plugins.entries..config.startupTimeoutMs โ†’ Plugin lifecycle layer โœ“ plugins.entries..config.initTimeoutMs โ†’ NOT IMPLEMENTED โœ—

Code Location Reference

The hardcoded constant typically resides in:

packages/mcp-client/src/connection.ts  // or
packages/mcp-runtime/src/client.ts

๐Ÿ› ๏ธ Step-by-Step Fix

Modify the plugin entry configuration to support a new initTimeoutMs parameter.

Configuration Before

json { “plugins”: { “entries”: { “neuralmemory”: { “enabled”: true, “config”: { “timeout”: 90000 } } } } }

Configuration After

json { “plugins”: { “entries”: { “neuralmemory”: { “enabled”: true, “config”: { “timeout”: 90000, “initTimeoutMs”: 90000 } } } } }

Option B: Global MCP Init Timeout

Add a global configuration parameter for all MCP plugins.

Configuration Before

json { “plugins”: { “mcpInitTimeoutMs”: 30000 } }

Configuration After

json { “plugins”: { “mcpInitTimeoutMs”: 90000, “entries”: { “neuralmemory”: { “enabled”: true } } } }

Option C: Runtime Override (Temporary Fix)

If the fix is not yet deployed, create a local override using an environment variable:

bash export OPENCLAW_MCP_INIT_TIMEOUT_MS=90000 openclaw start

Code Fix Implementation

To implement the fix in the codebase:

Step 1: Update MCP Client to Accept Timeout Parameter

typescript // packages/mcp-client/src/connection.ts

interface McpClientOptions { // … existing options initTimeoutMs?: number; }

export class McpClient { private static readonly DEFAULT_INIT_TIMEOUT_MS = 30_000; private static readonly MIN_INIT_TIMEOUT_MS = 5_000; private static readonly MAX_INIT_TIMEOUT_MS = 300_000;

constructor(private options: McpClientOptions) {}

private getEffectiveInitTimeout(): number { const configured = this.options.initTimeoutMs ?? process.env.OPENCLAW_MCP_INIT_TIMEOUT_MS;

if (configured === undefined) {
  return McpClient.DEFAULT_INIT_TIMEOUT_MS;
}

const timeout = Number(configured);
return Math.min(
  McpClient.MAX_INIT_TIMEOUT_MS,
  Math.max(McpClient.MIN_INIT_TIMEOUT_MS, timeout)
);

}

async initialize(): Promise { const timeoutMs = this.getEffectiveInitTimeout();

const initializePromise = this.performInitializeHandshake();
const timeoutPromise = new Promise<never>((_, reject) => {
  setTimeout(() => {
    reject(new Error(`MCP timeout: initialize (${timeoutMs}ms)`));
  }, timeoutMs);
});

await Promise.race([initializePromise, timeoutPromise]);

} }

Step 2: Propagate Config to MCP Client

typescript // packages/plugin-service/src/plugin-loader.ts

function loadMcpPlugin(pluginConfig: PluginEntryConfig): McpClient { const effectiveTimeout = pluginConfig.config?.initTimeoutMs ?? process.env.OPENCLAW_MCP_INIT_TIMEOUT_MS ?? undefined;

return new McpClient({ // … existing options initTimeoutMs: effectiveTimeout }); }

๐Ÿงช Verification

Verification Step 1: Confirm Timeout Configuration Loading

Execute the debug command to verify the timeout is being read:

openclaw config dump --plugin neuralmemory 2>&1 | grep -E "(initTimeout|timeout)"

Expected output after fix:

  "initTimeoutMs": 90000,
  "timeout": 90000

Verification Step 2: Confirm MCP Client Receives Timeout

Run with debug logging enabled:

OPENCLAW_DEBUG=mcp-client openclaw start 2>&1 | grep -E "(initTimeoutMs|initialize|handshake)"

Expected output:

[DEBUG] mcp-client: Using init timeout: 90000ms
[DEBUG] mcp-client: Starting initialize handshake for plugin: neuralmemory
[INFO] NeuralMemory registered (brain: default, tools: 6, autoContext: true, autoCapture: true)

Verification Step 3: Plugin Initialization Success

Confirm the plugin initializes without timeout error:

openclaw status --plugin neuralmemory

Expected output:

Plugin: neuralmemory
Status: RUNNING
Uptime: 42s
Tools: 6
Init Time: 12.4s

Exit code must be 0.

Verification Step 4: Simulate Long Initialization

To test the timeout boundary, temporarily set a shorter timeout and verify the error occurs at the expected boundary:

openclaw config set plugins.entries.neuralmemory.config.initTimeoutMs 5000
openclaw start 2>&1 | grep -E "(timeout|5000ms)"

Expected output (should show timeout at 5s, not 30s):

[ERROR] MCP timeout: initialize (5000ms)

Restore the correct timeout after verification:

openclaw config set plugins.entries.neuralmemory.config.initTimeoutMs 90000

โš ๏ธ Common Pitfalls

Pitfall 1: Confusing timeout with initTimeoutMs

The timeout configuration parameter only governs post-initialization request timeouts, not the handshake timeout.

Incorrect assumption:

"config": { "timeout": 120000 }  // Does NOT affect MCP init

Correct configuration:

"config": { 
  "timeout": 120000,           // MCP request timeout (post-init)
  "initTimeoutMs": 120000      // MCP handshake timeout (init)
}

Pitfall 2: Environment Variable Syntax Errors

When using environment variables, ensure correct casing and type:

Incorrect:

export OPENCLAW_MCP_INIT_TIMEOUT=90000  # Wrong: missing "Ms" suffix

Correct:

export OPENCLAW_MCP_INIT_TIMEOUT_MS=90000

Pitfall 3: Timeout Value Out of Bounds

The implementation enforces minimum and maximum bounds. Values outside the range are clamped:

Configured ValueEffective ValueReason
5005000Minimum enforced: 5000ms
600000300000Maximum enforced: 300000ms
NaN30000Default fallback

Pitfall 4: Configuration File vs. Runtime Override Priority

When multiple timeout sources are present, the priority order is:

  1. Plugin-specific config: plugins.entries.<id>.config.initTimeoutMs
  2. Environment variable: OPENCLAW_MCP_INIT_TIMEOUT_MS
  3. Global config: plugins.mcpInitTimeoutMs
  4. Hardcoded default: 30000

Pitfall 5: Docker Container Timeouts

When running inside Docker, ensure the container has sufficient resources for ML model loading:

# docker-compose.yml
services:
  openclaw:
    deploy:
      resources:
        limits:
          memory: 4G  # NeuralMemory requires adequate memory for sentence-transformers

Insufficient memory causes longer load times, exacerbating timeout issues.

Pitfall 6: Neural Memory Model Caching

The successful retry occurs because the model is cached after first load. To ensure consistent first-attempt success in production:

  • Pre-warm the plugin during deployment: openclaw plugin warmup neuralmemory
  • Use initTimeoutMs: 120000 for initial deployment
  • Reduce to initTimeoutMs: 30000 after cache is populated
  • MCP timeout: initialize (30000ms)
    Primary error. Hardcoded timeout exceeded during MCP handshake.
  • MCP timeout: request (timeoutMs)
    Post-initialization timeout. Governed by plugins.entries.<id>.config.timeout.
  • plugin service failed (neuralmemory-mcp)
    Plugin service marks plugin as failed after MCP init timeout.
  • Issue #412: Plugin startup timeout not propagated to MCP client
    Feature request to propagate startupTimeoutMs to MCP client layer. Closed as duplicate of this issue.
  • Issue #387: NeuralMemory fails to initialize on cold start
    Documented the 30s hardcoded timeout as root cause. Workaround documented.
  • Issue #156: MCP client should support configurable timeouts per plugin
    Original architecture discussion for timeout configuration.

External Dependencies

  • NeuralMemory GitHub: Issue #18
    Plugin-side tracking for optimizing model loading time to fit within 30s window.
  • @modelcontextprotocol/sdk: Timeout handling
    Upstream SDK does not expose timeout configuration; handled at wrapper layer.

Evidence & Sources

This troubleshooting guide was automatically synthesized by the FixClaw Intelligence Pipeline from community discussions.