【Bug已解决】openclaw: "Plugin load failed" / TypeError: Cannot read property 'name' of undefined — OpenClaw 插件加载失败解决方案

1. 问题描述

OpenClaw 在启动或运行过程中无法加载插件,报出类型错误:

# 插件加载失败 - 标准报错
$ openclaw "分析代码"
Error: Plugin load failed
TypeError: Cannot read property 'name' of undefined

# 插件入口找不到
$ openclaw --plugin custom-tools "task"
Error: Cannot find module './plugins/custom-tools'
plugin entry not found in plugin directory

# 插件 API 版本不兼容
$ openclaw "task"
Error: Plugin API version mismatch
Expected v3, got v1. Plugin 'code-formatter' is incompatible.

# 插件初始化崩溃
$ openclaw "task"
Error: Plugin 'test-runner' initialization failed
TypeError: openclaw.registerCommand is not a function

这个问题在以下场景中特别常见:

  • 插件 API 版本与 OpenClaw 主程序不匹配
  • 插件目录结构不符合规范
  • 插件 manifest.json 缺少必要字段
  • 插件代码中有语法错误或依赖缺失
  • 插件加载顺序导致依赖未就绪
  • 升级 OpenClaw 后旧插件 API 变更

2. 原因分析

核心原理拆解

OpenClaw 启动 → 扫描插件目录 → 读取 manifest.json
        ↓
验证 API 版本 ←──── 检查插件入口文件
        ↓
执行 plugin.init() ←──── 注入 API 对象
        ↓
init() 中访问未定义属性 → 抛出 TypeError

OpenClaw 插件加载机制说明

OpenClaw 的插件系统基于 manifest.json 描述符 + JavaScript 入口文件的架构。启动时,插件管理器会:

  1. 扫描 ~/.openclaw/plugins/ 目录下的所有子目录
  2. 读取每个子目录中的 manifest.json 文件
  3. 验证 manifest 中的 apiVersion 是否与主程序兼容
  4. 根据 manifest 中的 entry 字段加载入口 JS 文件
  5. 调用入口文件导出的 init(api) 函数,传入 API 对象
  6. 插件通过 api.registerCommand()api.registerTool() 等方法注册功能

如果 manifest.json 缺少必要字段(如 name),入口文件不存在,或 init() 函数中访问了未注入的 API 方法,就会触发上述错误。

原因分类表

原因分类 具体表现 占比
API 版本不兼容 registerCommand is not a function 约 30%
manifest 缺字段 Cannot read property 'name' 约 25%
入口文件缺失 Cannot find module 约 20%
依赖未安装 Cannot find module 'xxx' 约 15%
加载顺序错误 依赖插件未初始化 约 5%
语法错误 Unexpected token 约 5%

3. 解决方案

方案一:验证并修复 manifest.json(最推荐)

# 步骤 1:查看插件目录结构
ls -la ~/.openclaw/plugins/
ls -la ~/.openclaw/plugins/*/

# 步骤 2:检查 manifest.json 是否存在且格式正确
for plugin in ~/.openclaw/plugins/*/; do
    echo "=== $(basename $plugin) ==="
    if [ -f "$plugin/manifest.json" ]; then
        python3 -m json.tool "$plugin/manifest.json" 2>&1
    else
        echo "❌ manifest.json 不存在"
    fi
    echo ""
done

# 步骤 3:修复缺失字段
# 正确的 manifest.json 示例:
cat > ~/.openclaw/plugins/custom-tools/manifest.json << 'EOF'
{
    "name": "custom-tools",
    "version": "1.0.0",
    "apiVersion": "3",
    "entry": "./index.js",
    "description": "自定义工具插件",
    "author": "developer",
    "dependencies": []
}
EOF

# 步骤 4:验证修复
openclaw --debug 2>&1 | grep -i "plugin\|manifest"
openclaw "测试"

方案二:修复 API 版本兼容性

# 步骤 1:查看 OpenClaw 当前版本和 API 版本
openclaw --version
openclaw --api-version

# 步骤 2:查看插件的 API 版本
cat ~/.openclaw/plugins/*/manifest.json | grep -i apiVersion

# 步骤 3:如果 API 版本不匹配,更新插件
# 方式1: 更新插件到兼容版本
cd ~/.openclaw/plugins/custom-tools/
npm update

# 方式2: 修改 manifest.json 中的 apiVersion
python3 -c "
import json
import os

plugin_dir = os.path.expanduser('~/.openclaw/plugins/custom-tools')
manifest_path = os.path.join(plugin_dir, 'manifest.json')

with open(manifest_path, 'r') as f:
    manifest = json.load(f)

# 查看当前 API 版本
print(f'当前 apiVersion: {manifest.get(\"apiVersion\", \"未设置\")}')

# 更新为当前主程序兼容的版本
manifest['apiVersion'] = '3'

with open(manifest_path, 'w') as f:
    json.dump(manifest, f, indent=4)
print('apiVersion 已更新为 3')
"

# 步骤 4:验证
openclaw --debug 2>&1 | grep -i "api.*version\|compat"
openclaw "测试"

方案三:修复入口文件和依赖

# 步骤 1:验证入口文件存在
PLUGIN_DIR=~/.openclaw/plugins/custom-tools
ENTRY=$(python3 -c "import json; print(json.load(open('$PLUGIN_DIR/manifest.json'))['entry'])")
echo "入口文件: $PLUGIN_DIR/$ENTRY"

if [ ! -f "$PLUGIN_DIR/$ENTRY" ]; then
    echo "❌ 入口文件不存在,创建基本模板"
    mkdir -p "$PLUGIN_DIR"
    cat > "$PLUGIN_DIR/index.js" << 'JAVASCRIPT'
module.exports = {
    init: function(api) {
        // 注册自定义命令
        api.registerCommand('custom', (args) => {
            return 'Custom command executed: ' + args.join(' ');
        });
        
        // 注册工具
        if (api.registerTool) {
            api.registerTool('formatter', {
                description: 'Format code',
                execute: (input) => input.trim()
            });
        }
        
        console.log('Plugin custom-tools loaded successfully');
    },
    
    destroy: function() {
        console.log('Plugin custom-tools destroyed');
    }
};
JAVASCRIPT
fi

# 步骤 2:安装插件依赖
cd "$PLUGIN_DIR"
if [ -f "package.json" ]; then
    npm install
else
    echo "无 package.json,跳过依赖安装"
fi

# 步骤 3:验证入口文件语法
node -c "$PLUGIN_DIR/index.js"
# 无输出 = 语法正确

# 步骤 4:测试加载
openclaw --debug 2>&1 | grep -i "plugin.*load\|plugin.*init"
openclaw "测试"

方案四:修复插件加载顺序

# 步骤 1:查看插件加载顺序
openclaw --debug 2>&1 | grep -i "plugin.*order\|plugin.*load" | head -20

# 步骤 2:检查插件间依赖关系
for plugin in ~/.openclaw/plugins/*/; do
    name=$(basename "$plugin")
    deps=$(python3 -c "
import json, sys
try:
    m = json.load(open('$plugin/manifest.json'))
    print(','.join(m.get('dependencies', [])))
except: print('')
" 2>/dev/null)
    echo "$name → depends on: ${deps:-无}"
done

# 步骤 3:手动配置加载顺序
cat > ~/.openclaw/plugin-config.json << 'EOF'
{
    "loadOrder": [
        "core-utils",
        "file-reader",
        "code-formatter",
        "test-runner",
        "custom-tools"
    ],
    "timeout": 5000,
    "continueOnError": false
}
EOF

# 步骤 4:验证加载顺序
openclaw --debug 2>&1 | grep -i "plugin.*order"
openclaw "测试"

方案五:使用 --safe-mode 隔离问题插件

# 步骤 1:安全模式启动(禁用所有第三方插件)
openclaw --safe-mode "测试任务"

# 步骤 2:如果安全模式正常,逐个启用插件排查
openclaw --safe-mode --enable-plugin core-utils "测试"
openclaw --safe-mode --enable-plugin core-utils --enable-plugin file-reader "测试"

# 步骤 3:或批量禁用后逐个启用
openclaw --disable-all-plugins "测试"

# 启用单个插件测试
openclaw --enable-only custom-tools "测试"

# 步骤 4:找到问题插件后删除或修复
PLUGIN_NAME="custom-tools"
rm -rf ~/.openclaw/plugins/$PLUGIN_NAME/
# 或备份后修复
mv ~/.openclaw/plugins/$PLUGIN_NAME/ ~/.openclaw/plugins/$PLUGIN_NAME.bak/

# 步骤 5:验证
openclaw "测试"

方案六:编写插件兼容性检查工具

#!/usr/bin/env python3
"""OpenClaw 插件兼容性检查工具"""
import json
import os
import sys
import subprocess

class PluginChecker:
    def __init__(self, plugin_dir=None):
        self.plugin_dir = plugin_dir or os.path.expanduser('~/.openclaw/plugins')
        self.errors = []
        self.warnings = []
        
    def get_api_version(self):
        """获取 OpenClaw 当前 API 版本"""
        try:
            result = subprocess.run(
                ['openclaw', '--api-version'],
                capture_output=True, text=True, timeout=5
            )
            return result.stdout.strip()
        except Exception:
            return None
    
    def check_manifest(self, plugin_path):
        """检查 manifest.json"""
        manifest_path = os.path.join(plugin_path, 'manifest.json')
        checks = {'valid': True, 'errors': [], 'warnings': []}
        
        # 检查文件是否存在
        if not os.path.exists(manifest_path):
            checks['valid'] = False
            checks['errors'].append('manifest.json 不存在')
            return checks
        
        # 检查 JSON 格式
        try:
            with open(manifest_path, 'r') as f:
                manifest = json.load(f)
        except json.JSONDecodeError as e:
            checks['valid'] = False
            checks['errors'].append(f'JSON 格式错误: {e}')
            return checks
        
        # 检查必要字段
        required_fields = ['name', 'version', 'apiVersion', 'entry']
        for field in required_fields:
            if field not in manifest:
                checks['valid'] = False
                checks['errors'].append(f'缺少必要字段: {field}')
            elif not manifest[field]:
                checks['valid'] = False
                checks['errors'].append(f'字段为空: {field}')
        
        # 检查入口文件
        if 'entry' in manifest:
            entry_path = os.path.join(plugin_path, manifest['entry'])
            if not os.path.exists(entry_path):
                checks['valid'] = False
                checks['errors'].append(f'入口文件不存在: {manifest["entry"]}')
            else:
                # 检查 JS 语法
                result = subprocess.run(
                    ['node', '-c', entry_path],
                    capture_output=True, text=True, timeout=5
                )
                if result.returncode != 0:
                    checks['valid'] = False
                    checks['errors'].append(f'入口文件语法错误: {result.stderr.strip()}')
        
        # 检查 API 版本兼容性
        current_api = self.get_api_version()
        if current_api and 'apiVersion' in manifest:
            if manifest['apiVersion'] != current_api:
                checks['warnings'].append(
                    f'API 版本不匹配: 插件={manifest["apiVersion"]}, '
                    f'主程序={current_api}'
                )
        
        # 检查依赖
        if 'dependencies' in manifest:
            for dep in manifest['dependencies']:
                dep_path = os.path.join(self.plugin_dir, dep)
                if not os.path.exists(dep_path):
                    checks['warnings'].append(f'依赖插件不存在: {dep}')
        
        return checks
    
    def check_all(self):
        """检查所有插件"""
        if not os.path.exists(self.plugin_dir):
            print(f'插件目录不存在: {self.plugin_dir}')
            return
        
        plugins = [d for d in os.listdir(self.plugin_dir)
                   if os.path.isdir(os.path.join(self.plugin_dir, d))]
        
        if not plugins:
            print('未找到任何插件')
            return
        
        print(f'检查 {len(plugins)} 个插件...\n')
        
        for plugin_name in sorted(plugins):
            plugin_path = os.path.join(self.plugin_dir, plugin_name)
            result = self.check_manifest(plugin_path)
            
            status = '✅' if result['valid'] else '❌'
            print(f'{status} {plugin_name}')
            
            for err in result['errors']:
                print(f'   错误: {err}')
                self.errors.append(f'{plugin_name}: {err}')
            
            for warn in result['warnings']:
                print(f'   警告: {warn}')
                self.warnings.append(f'{plugin_name}: {warn}')
            
            print()
        
        # 总结
        print('=' * 50)
        print(f'总计: {len(plugins)} 个插件')
        print(f'错误: {len(self.errors)} 个')
        print(f'警告: {len(self.warnings)} 个')
        
        if self.errors:
            print('\n需要修复的插件:')
            for err in self.errors:
                print(f'  - {err}')

if __name__ == '__main__':
    checker = PluginChecker()
    checker.check_all()

4. 各方案对比总结

方案 适用场景 推荐指数 难度
方案一:修复 manifest 缺字段 ⭐⭐⭐⭐⭐
方案二:修复 API 版本 版本不兼容 ⭐⭐⭐⭐⭐
方案三:修复入口文件 文件缺失 ⭐⭐⭐⭐
方案四:修复加载顺序 依赖问题 ⭐⭐⭐⭐
方案五:安全模式 隔离排查 ⭐⭐⭐⭐⭐
方案六:检查工具 长期运维 ⭐⭐⭐

5. 常见问题 FAQ

5.1 升级 OpenClaw 后所有插件都报错

OpenClaw 大版本升级可能引入 API breaking changes。查看变更日志确认 API 变化,批量更新所有插件的 apiVersion 和 API 调用方式。

# 查看变更日志
openclaw --changelog | head -100

# 批量更新 apiVersion
for manifest in ~/.openclaw/plugins/*/manifest.json; do
    python3 -c "
import json
with open('$manifest', 'r') as f:
    m = json.load(f)
m['apiVersion'] = '3'
with open('$manifest', 'w') as f:
    json.dump(m, f, indent=4)
print(f'Updated: $manifest')
"
done

5.2 Windows 上插件路径问题

Windows 路径分隔符是 \,manifest.json 中的 entry 字段可能使用了错误的分隔符:

# 检查 manifest.json 中的 entry 路径
Get-Content ~/.openclaw/plugins/custom-tools/manifest.json | ConvertFrom-Json | Select-Object entry

# 确保使用正斜杠或双反斜杠
# 正确: "entry": "./index.js" 或 "entry": ".\\index.js"
# 错误: "entry": ".\index.js" (单反斜杠会被转义)

5.3 插件依赖的 npm 包未安装

插件入口文件 require() 了外部包但未安装:

# 进入插件目录安装依赖
cd ~/.openclaw/plugins/custom-tools/
npm install

# 或检查 package.json 依赖
cat package.json | python3 -m json.tool

# 批量安装所有插件依赖
for plugin in ~/.openclaw/plugins/*/; do
    if [ -f "$plugin/package.json" ]; then
        echo "Installing deps for $(basename $plugin)..."
        cd "$plugin" && npm install --production
    fi
done

5.4 插件 init() 中 API 对象为空

OpenClaw 注入的 API 对象可能在不同版本中有不同结构。使用可选链或防御性检查:

module.exports = {
    init: function(api) {
        // ❌ 错误写法:直接调用可能不存在的方法
        // api.registerCommand('test', handler);
        
        // ✅ 正确写法:先检查方法是否存在
        if (api && typeof api.registerCommand === 'function') {
            api.registerCommand('test', (args) => 'test: ' + args);
        }
        
        if (api && typeof api.registerTool === 'function') {
            api.registerTool('formatter', { execute: (s) => s.trim() });
        }
        
        // 使用 API 版本号做兼容
        const version = api?.version || 1;
        if (version >= 3 && api.registerHook) {
            api.registerHook('beforeSend', (ctx) => { /* ... */ });
        }
    }
};

5.5 多个插件注册了同名命令

两个插件注册了相同的命令名导致冲突:

# 查看命令冲突
openclaw --debug 2>&1 | grep -i "conflict\|duplicate"

# 在 manifest.json 中使用命名空间
{
    "name": "custom-tools",
    "namespace": "custom",
    "commands": ["custom:format", "custom:lint"]
}

# 或在插件配置中设置优先级
cat > ~/.openclaw/plugin-config.json << 'EOF'
{
    "commandPriority": {
        "format": "code-formatter",
        "lint": "custom-tools"
    }
}
EOF

5.6 Docker 中插件目录未挂载

Docker 容器内插件目录是空的:

# 挂载宿主机插件目录到容器
docker run -it --rm \
    -v ~/.openclaw/plugins:/root/.openclaw/plugins \
    -v ~/.openclaw/config.json:/root/.openclaw/config.json \
    openclaw-image

# 或在 Dockerfile 中 COPY 插件
COPY plugins/ /root/.openclaw/plugins/

# 验证
docker run --rm openclaw-image openclaw --list-plugins

5.7 插件加载超时

某些插件 init() 执行耗时操作导致超时:

# 调整插件加载超时
export OPENCLAW_PLUGIN_TIMEOUT=30000  # 30秒

# 或在配置中设置
python3 -c "
import json
with open(os.path.expanduser('~/.openclaw/plugin-config.json'), 'r') as f:
    config = json.load(f)
config['timeout'] = 30000
config['asyncInit'] = True  # 允许异步初始化
with open(os.path.expanduser('~/.openclaw/plugin-config.json'), 'w') as f:
    json.dump(config, f, indent=2)
print('超时和异步初始化已配置')
"

# 在插件中使用异步初始化
# index.js
module.exports = {
    init: async function(api) {
        await loadLargeDataset();
        api.registerCommand('search', (args) => search(args));
    }
};

5.8 如何查看已加载的插件列表

# 列出所有已安装插件
openclaw --list-plugins

# 查看插件详细信息
openclaw --plugin-info custom-tools

# 查看插件加载日志
openclaw --debug 2>&1 | grep -i "plugin"

5.9 插件内存泄漏

插件未正确实现 destroy() 方法导致内存泄漏:

// 确保插件有 destroy 方法
module.exports = {
    init: function(api) {
        this.timer = setInterval(() => { /* ... */ }, 1000);
        api.registerCommand('start', () => this.timer.start());
    },
    
    destroy: function() {
        // 清理定时器、事件监听器等
        if (this.timer) {
            clearInterval(this.timer);
        }
        console.log('Plugin cleaned up');
    }
};
# 检查插件内存使用
openclaw --debug --profile-plugins 2>&1 | grep -i "memory\|leak"

5.10 排查清单速查表

□ 1. 检查 manifest.json 是否存在且格式正确
□ 2. 确认 name/version/apiVersion/entry 字段完整
□ 3. 验证入口文件路径存在
□ 4. node -c 检查入口文件语法
□ 5. openclaw --api-version 对比插件 apiVersion
□ 6. 检查插件 npm 依赖是否安装
□ 7. openclaw --safe-mode 隔离问题插件
□ 8. openclaw --debug 查看加载顺序
□ 9. 检查插件间依赖关系
□ 10. 确认 init() 中 API 方法做了存在性检查

6. 总结

  1. 根本原因:插件加载失败最常见原因是 API 版本不兼容(30%)和 manifest.json 缺字段(25%)
  2. 最佳实践:使用 openclaw --debug 2>&1 | grep plugin 诊断具体加载步骤
  3. manifest 修复:确保 nameversionapiVersionentry 四个字段完整且值有效
  4. 安全模式:使用 openclaw --safe-mode 快速隔离问题插件,再逐个启用排查
  5. 最佳实践建议:编写插件时对 API 方法做防御性检查(typeof api.registerCommand === 'function'),并始终实现 destroy() 方法避免内存泄漏

故障排查流程图

flowchart TD
    A[插件加载失败] --> B[检查 manifest.json]
    B --> C[python3 -m json.tool 验证]
    C --> D{格式正确?}
    D -->|否| E[修复 JSON 格式]
    D -->|是| F[检查必要字段]
    E --> G[重新加载测试]
    F --> H{name/version/apiVersion/entry 齐全?}
    H -->|否| I[补充缺失字段]
    H -->|是| J[验证入口文件]
    I --> G
    J --> K{入口文件存在?}
    K -->|否| L[创建入口文件]
    K -->|是| M[node -c 检查语法]
    L --> G
    M --> N{语法正确?}
    N -->|否| O[修复语法错误]
    N -->|是| P[检查 API 版本]
    O --> G
    P --> Q{版本兼容?}
    Q -->|否| R[更新 apiVersion]
    Q -->|是| S[检查依赖]
    R --> G
    S --> T{依赖就绪?}
    T -->|否| U[npm install]
    T -->|是| V[安全模式测试]
    U --> G
    V --> W{安全模式正常?}
    W -->|是| X[逐个启用排查]
    W -->|否| Y[检查核心配置]
    X --> Z[找到问题插件]
    Z --> AA[修复或移除]
    AA --> G
    G --> BB{加载成功?}
    BB -->|是| CC[✅ 问题解决]
    BB -->|否| DD[使用兼容性检查工具]
    DD --> CC
Logo

中国智能体开发者社区,聚焦智能体与大模型开发,提供前沿资讯、实用工具链、开源项目及行业案例。通过技术沙龙、开发者大赛等活动,促进经验交流与协作,助力开发者快速构建创新智能应用。

更多推荐