Fix clw-router-crash: OpenClaw Router Initialization Failure

OpenClaw intermediate Node.js Browser Universal/Isomorphic

1. Symptoms

The clw-router-crash error manifests during the application bootstrap phase, specifically when OpenClaw attempts to initialize its routing subsystem. This error typically halts application startup and produces a distinctive stack trace that points to the router initialization code.

Primary indicators include:

[OpenClaw] FATAL: Router initialization failed
[OpenClaw] Error: clw-router-crash
    at Router.initialize (/node_modules/openclaw/lib/router.js:142:15)
    at Application.bootstrap (/node_modules/openclaw/lib/app.js:89:22)
    at main (/app/index.js:12:5)

UnhandledPromiseRejectionWarning: This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise that was not handled with .catch().

Additional symptoms you may observe:

  • Application hangs indefinitely at the bootstrap stage without producing output
  • Incomplete middleware chain execution where requests fail immediately
  • Router state remains null or undefined after initialization attempts
  • Memory consumption spikes briefly before the crash, indicating a circular reference or infinite loop during route registration
  • Partial route registration where only some routes appear in the routing table

In development environments with hot-reloading enabled, you may encounter this error when modifying route definitions without properly cleaning up the previous router instance. The crash typically occurs before any HTTP server begins listening, making early detection critical.

2. Root Cause

The clw-router-crash error originates from three distinct failure patterns in OpenClaw’s router initialization pipeline. Understanding these root causes is essential for implementing the correct remediation strategy.

Pattern A: Circular Dependency in Route Handlers

OpenClaw’s router uses a dependency injection container to resolve route handlers. When a route handler imports a module that transitively imports the router itself, the initialization sequence creates an infinite recursion. The router attempts to resolve dependencies while still being constructed, resulting in an unhandled rejection that manifests as the crash error.

Pattern B: Invalid Middleware Configuration

The router’s middleware chain must be defined as an ordered array where each middleware is either a function or a string reference to a registered middleware. When the configuration contains malformed entries—such as objects instead of functions, undefined references, or circular middleware dependencies—the initialization validation fails at step three of the bootstrap sequence.

// Invalid configuration that triggers clw-router-crash
const router = new Router({
  middleware: [
    'auth',           // Valid string reference
    null,             // Invalid: null entry
    require('./bad'), // Invalid: module exports undefined
    function(req, res, next) { next(); } // Valid function
  ]
});

Pattern C: Missing Required Lifecycle Hooks

OpenClaw’s router requires certain lifecycle hooks to be defined or explicitly marked as optional. When custom routers extend the base Router class without calling the parent constructor or omitting required hook implementations, the internal state machine enters an invalid state that triggers the crash when the router attempts to process its first request.

3. Step-by-Step Fix

Follow these remediation steps in order. Complete each step fully before proceeding to the next, as the root cause determination depends on your specific configuration.

Step 1: Audit Route Handler Dependencies

Create a dependency graph of your route handlers to identify circular imports. Use a tool like madge to visualize the dependency tree:

npx madge --circular --extensions js ./src/routes

Before:

// src/routes/users.js
const router = require('../router'); // Creates circular dependency
const { getUsers } = require('../services/userService');

router.get('/users', getUsers);
module.exports = router;

After:

// src/routes/users.js
const { getUsers } = require('../services/userService');

// Export route configuration, not the router instance
module.exports = (router) => {
  router.get('/users', getUsers);
};

Step 2: Validate Middleware Chain Configuration

Ensure your middleware array contains only valid entries. Remove null values, undefined references, and non-function objects.

Before:

const app = new Application({
  router: {
    middleware: [
      'session',
      null,              // Problematic entry
      require('./mod'),  // Problematic: may export undefined
      'rateLimit'
    ]
  }
});

After:

const app = new Application({
  router: {
    middleware: [
      'session',
      function sessionHandler(req, res, next) {
        // Implement inline or import from verified module
        req.session = {};
        next();
      },
      'rateLimit'
    ]
  }
});

Step 3: Register Middleware Modules Correctly

If you use string references to middleware, ensure they are registered with the application container before router initialization.

Before:

// index.js
const app = new Application();
app.setRouter(new Router()); // Router references 'auth' middleware not yet registered

After:

// index.js
const app = new Application();

// Register middleware before router initialization
app.use('auth', require('./middleware/auth'));
app.use('rateLimit', require('./middleware/rateLimit'));

// Now initialize router
app.setRouter(new Router());

Step 4: Fix Extended Router Implementations

If you have custom router classes extending OpenClaw’s base Router, ensure proper constructor chaining and lifecycle hook definitions.

Before:

class CustomRouter extends Router {
  constructor(config) {
    // Missing super() call causes crash
    this.config = config;
  }
}

After:

class CustomRouter extends Router {
  constructor(config) {
    super(config); // Call parent constructor first
    this.config = config;
  }

  // Implement required lifecycle hooks or mark as optional
  async onRouteNotFound(context) {
    // Custom handling or delegate to parent
    return super.onRouteNotFound(context);
  }
}

4. Verification

After applying the fix, verify that the router initializes correctly by running the following checks:

Immediate Verification:

node --check ./src/app.js
node ./src/app.js

If the application starts without the clw-router-crash error, the immediate issue is resolved. Look for the server listening message:

[OpenClaw] Router initialized successfully in 45ms
[OpenClaw] Server listening on port 3000

Structural Verification:

Create a test script to validate router state after initialization:

// test-router-init.js
const { Application } = require('openclaw');
const { Router } = require('openclaw');

async function verifyRouter() {
  const app = new Application();
  app.use('test', (req, res, next) => next());
  app.setRouter(new Router());

  await app.bootstrap();

  const router = app.getRouter();
  
  if (!router) {
    throw new Error('Router not initialized');
  }

  if (typeof router.resolve !== 'function') {
    throw new Error('Router missing resolve method');
  }

  console.log('Router state:', {
    routes: router.routes?.length ?? 0,
    middleware: router.middleware?.length ?? 0,
    initialized: router._initialized ?? false
  });

  console.log('✓ Router verification passed');
  process.exit(0);
}

verifyRouter().catch(err => {
  console.error('✗ Router verification failed:', err.message);
  process.exit(1);
});

Run the verification script:

node test-router-init.js

Integration Test:

# Start the application in background
node ./src/app.js &
APP_PID=$!

# Wait for startup
sleep 2

# Test route registration
curl -s http://localhost:3000/health | jq .

# Clean up
kill $APP_PID

5. Common Pitfalls

Pitfall 1: Ignoring Order of Operations

Many developers initialize the router before registering all middleware. The router holds a reference to middleware names during initialization but does not validate their existence until the first request arrives. This delayed validation makes debugging difficult. Always register middleware before calling setRouter().

Pitfall 2: Hot Reload Without Cleanup

When using development tools with hot module replacement, failing to unregister the previous router instance before loading a new one creates duplicate router registrations. Always implement cleanup logic in your development server configuration:

if (currentRouter) {
  app.removeRouter();
  currentRouter = null;
}

Pitfall 3: Confusing Module Exports

OpenClaw expects middleware to export a function. If your module exports an object containing the function rather than the function itself, the router cannot invoke it correctly. Always verify your module exports match the expected interface:

// Correct
module.exports = function(req, res, next) { next(); };

// Incorrect - exports object with function
module.exports = { handler: function(req, res, next) { next(); } };

Pitfall 4: Async Middleware Without Promise Handling

If your middleware returns promises but does not call next(), the router waits indefinitely. All middleware must either call next() or reject with an error. Async middleware must resolve or invoke the callback:

// Correct async middleware
const asyncMiddleware = async (req, res, next) => {
  try {
    req.data = await fetchData();
    next();
  } catch (err) {
    next(err);
  }
};

Pitfall 5: Skipping Error Boundary Middleware

Without an error-handling middleware as the final entry in your middleware chain, unhandled errors in route handlers propagate as unhandled rejections. While this does not directly cause clw-router-crash, it creates instability that may manifest as router crashes under load.

clw-handler-undefined

This error occurs when OpenClaw attempts to invoke a route handler that is undefined or null. Unlike clw-router-crash, which occurs during initialization, clw-handler-undefined manifests when a request matches a route but the handler cannot be resolved. This typically results from incorrectly passing handler names instead of handler functions to route definitions, or from deleting handlers after registration without updating route configurations.

clw-middleware-chain-broken

This error indicates that the middleware chain contains a gap—typically a middleware that never calls next() or reject(). The router detects this condition when processing requests, as the request handler is never invoked. Symptoms include requests that hang indefinitely without timeout and no response returned to the client. Unlike clw-router-crash, this error occurs during request processing rather than initialization.

clw-route-not-registered

This error occurs when the router cannot find a matching route for an incoming request. The router successfully initializes but fails to match the request path and method against registered routes. This differs from clw-router-crash because the router itself is functioning—the issue lies in route registration scope, often caused by conditional route registration that fails under certain configuration states or environment variables.