musistudio/claude-code-router · warning

[plugin:${pluginId}] Rollback stop hook failed: ${formatErro

Error message

[plugin:${pluginId}] Rollback stop hook failed: ${formatError(error)}

What it means

During rollbackConfiguredPluginLoad, after a plugin's startup failed, each newly registered stop hook for that plugin is invoked with reason 'disabled' to undo partial registration; if one of those stop hooks throws, this warning is logged and rollback continues to the next hook and to backend resource cleanup.

Source

Thrown at packages/core/src/plugins/service.ts:916

  private async rollbackConfiguredPluginLoad(pluginId: string, snapshot: PluginServiceStateSnapshot): Promise<void> {
    const newStopHooks = this.stopHooks.slice(snapshot.stopHooks.length).reverse();
    this.apps = snapshot.apps;
    this.coreGatewayConfig = snapshot.coreGatewayConfig;
    this.coreProviderPlugins = snapshot.coreProviderPlugins;
    this.gatewayRequestTransforms = snapshot.gatewayRequestTransforms;
    this.gatewayRoutes = snapshot.gatewayRoutes;
    this.providerAccountConnectors = snapshot.providerAccountConnectors;
    this.proxyRoutes = snapshot.proxyRoutes;
    this.resourceOwnerIds = snapshot.resourceOwnerIds;
    this.stopHooks = snapshot.stopHooks;
    this.virtualModelProfiles = snapshot.virtualModelProfiles;

    for (const stopHook of newStopHooks) {
      try {
        await stopHook.stop({ reason: "disabled" });
      } catch (error) {
        console.warn(`[plugin:${pluginId}] Rollback stop hook failed: ${formatError(error)}`);
      }
    }

    try {
      await backendService.stopOwner(pluginId);
    } catch (error) {
      console.warn(`[plugin:${pluginId}] Rollback resource cleanup failed: ${formatError(error)}`);
    }
  }
}

export const pluginService = new GatewayPluginService();

function pluginPermissionAccess(pluginConfig: Pick<GatewayPluginConfig, "enabled" | "id" | "permissions">): PluginPermissionAccess {
  const permissions = pluginConfig.permissions
    ?? knownGatewayPluginDefaultPermissions(pluginConfig.id)
    ?? (pluginConfig.enabled === true ? undefined : [...GATEWAY_PLUGIN_PERMISSION_IDS]);
  return {

View on GitHub (pinned to 99f24806c6)

Solutions

  1. Fix the underlying startup failure first (error 546 carries the root cause) — rollback warnings then disappear.
  2. Make the plugin's stop hooks idempotent and null-safe so they never throw during undo.
  3. Upgrade the plugin/core pair for lifecycle fixes.

Example fix

// before
stop: async () => { await this.db.close(); } // throws if start failed before db was created
// after
stop: async () => { await this.db?.close().catch(() => {}); }
Defensive patterns

Strategy: try-catch

Validate before calling

null

Type guard

null

Try / catch

null

Prevention

When it happens

Trigger: A plugin fails mid-start after registering stop hooks, and one of those hooks itself throws during undo — commonly because the resource it closes was never fully created before the original failure.

Common situations: Half-initialized plugin state: start threw after partial setup; teardown assuming fields that are undefined; async cleanup racing the rollback.

Related errors


AI-assisted analysis of musistudio/claude-code-router@99f24806c6 (2026-08-27). Data as JSON: /api/errors/67e848da8f3de5dc. Report an issue: GitHub.