
1. OpenClaw 启动报 migration failed 与 Schema version mismatch 的真实场景你升级完 OpenClaw敲下启动命令终端直接甩出一行红字Error: Schema version mismatch Expected: 5, Actual: 3。或者更直接一点Error: migration failed后面跟着Column email already exists in table users。服务起不来日志里全是迁移相关的报错这时候你大概会想我什么都没改怎么就崩了OpenClaw 是一个带本地数据库的 Agent 运行框架它把会话、项目、审计记录都存在 SQLite 里。每次版本升级代码里期望的数据库结构Schema会变比如新增一张audit_log表、给users加个索引。这些变更靠迁移文件migration来落地。迁移系统维护一张_migrations历史表记录哪些迁移已经跑过。问题就出在这里代码升级了但数据库还停在旧版本或者迁移跑到一半被中断历史表和实际表结构对不上启动时校验直接失败。这个报错特别容易出现在几种场景版本升级后忘了执行迁移多个开发者同时改 Schema迁移文件顺序乱了生产环境迁移被 CtrlC 打断数据库卡在 v3 和 v4 之间迁移文件之间有依赖004_add_index依赖003_add_column但 003 没跑成功。我试过在 CI 里自动迁移结果两个 job 并行跑直接撞上Migration lock held by another process。从链路看OpenClaw 启动时会做三件事读代码里期望的 Schema 版本号读数据库_migrations表里的实际版本号比对。不一致就抛Schema version mismatch。如果版本号一致但迁移文件校验和变了或者迁移执行时对象已存在就抛migration failed。所以定位的核心是先搞清楚数据库现在到底在哪个版本再决定是补迁移、修历史表还是从备份恢复。这一篇不讲虚的直接给你可复制的 settings 配置片段、迁移前备份命令、版本对齐操作以及重跑迁移后的验证动作。目标只有一个让 OpenClaw 正常启动。2. 把 settings 改到 TaoToken 场景前置准备与配置片段在动手修数据库之前先把 OpenClaw 的模型接入配置理顺。很多迁移失败其实和模型无关但如果你用的是 TaoToken 这类聚合接入服务配置写错会导致启动时连模型都初始化不了报错混在一起更难排查。TaoToken 的官网是 https://taotoken.net/?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content API 地址是 https://taotoken.net/api 注意 API 地址后面不加 UTM 参数。OpenClaw 的配置文件通常在.openclaw/config.json或项目根目录的settings.json。你需要把模型接入部分改成 TaoToken 的 Base URL 和 Key。下面是一个可复制的 JSON 片段路径和字段名按 OpenClaw 实际结构来{ model: { provider: openai-compatible, baseUrl: https://taotoken.net/api, apiKey: sk-你的TaoToken密钥, modelId: claude-sonnet-4-20250514, timeout: 60000, maxRetries: 3 }, database: { path: .openclaw/data.db, backupBeforeMigrate: true }, migration: { autoMigrate: false, safeMode: true, backupBeforeMigrate: true, transactionPerMigration: true, stopOnError: true, lockTimeout: 60000, retryOnLock: true, maxRetries: 3, validateChecksums: true } }这里有几个关键点。baseUrl必须是https://taotoken.net/api不要写成带/v1的路径OpenClaw 的 openai-compatible 适配器会自动拼接。modelId填你实际要用的模型 ID比如 Claude 系列或 GPT 系列具体以 TaoToken 控制台里显示的为准。apiKey从 TaoToken 控制台的 API Keys 页面生成地址是 https://taotoken.net/api-keys?utm_sourcetaotoken_aicg_blog_endutm_contentapi_keysutm_campaignrewrite 。migration这一段是重点。autoMigrate设为false避免启动时自动迁移把问题搞得更复杂。safeMode开启后每个迁移用事务包装失败自动回滚。backupBeforeMigrate确保迁移前自动备份数据库。validateChecksums会校验迁移文件是否被改动过防止有人手改已应用的迁移导致版本对不上。如果你用的是 TOML 格式的配置比如config.toml等价写法如下[model] provider openai-compatible baseUrl https://taotoken.net/api apiKey sk-你的TaoToken密钥 modelId claude-sonnet-4-20250514 timeout 60000 maxRetries 3 [database] path .openclaw/data.db backupBeforeMigrate true [migration] autoMigrate false safeMode true backupBeforeMigrate true transactionPerMigration true stopOnError true lockTimeout 60000 retryOnLock true maxRetries 3 validateChecksums true改完配置后先别急着跑迁移。用openclaw --migration-status看一眼当前状态。如果输出里Current version和Expected version不一致说明确实需要迁移。如果Pending migrations列表里有多个注意它们的顺序依赖关系不能乱。注意TaoToken 的 API Key 不要硬编码在会提交到 Git 的文件里。可以用环境变量TAOTOKEN_API_KEY然后在配置里写apiKey: ${TAOTOKEN_API_KEY}OpenClaw 支持这种占位符替换。配置改好后模型接入和数据库迁移是两条独立的链路。模型配置错了启动时会报连接超时或 401数据库迁移错了报的是 Schema 版本不匹配。先把模型配置确认能通再专心修迁移排查起来会清晰很多。3. 可复制配置迁移前备份、版本对齐与重跑迁移的完整操作修迁移的核心思路是先备份再对齐版本最后安全重跑。下面每一步都可以直接复制执行。第一步迁移前备份。不管数据库现在是什么状态先复制一份出来。OpenClaw 自带备份命令openclaw --backup --before-migration如果命令不可用直接手动复制 SQLite 文件cp .openclaw/data.db .openclaw/data.db.bak.$(date %Y%m%d_%H%M%S)备份完成后检查迁移状态和历史openclaw --migration-status openclaw --migration-history--migration-status会输出当前版本、期望版本、待执行迁移列表。--migration-history会列出每个迁移文件的执行状态已应用的打勾待执行的显示 Pending。如果历史表里显示某个迁移已应用但实际数据库里对应的表或列不存在说明迁移被中断了历史表记录不完整。第二步检查数据库实际 Schema。用 Python 直接读 SQLite确认表和列的真实情况import sqlite3 conn sqlite3.connect(.openclaw/data.db) cursor conn.cursor() cursor.execute(SELECT name FROM sqlite_master WHERE typetable) tables cursor.fetchall() print(表:, [t[0] for t in tables]) try: cursor.execute(SELECT * FROM _migrations ORDER BY version) migrations cursor.fetchall() print(已应用迁移:) for m in migrations: print(f {m}) except sqlite3.OperationalError: print(无迁移历史表) cursor.execute(PRAGMA table_info(users)) columns cursor.fetchall() print(users 表列:) for col in columns: print(f {col[1]} ({col[2]})) conn.close()如果_migrations表里记录的版本和实际表结构对不上比如历史表说004_add_index已应用但users表里没有idx_users_email索引那就需要修复历史表。可以用openclaw --migration-mark手动标记openclaw --migration-mark 004_add_index --applied --force或者反过来如果实际对象已存在但历史表没记录标记为已应用跳过执行openclaw --migration-mark 004_add_index --applied第三步版本对齐。如果Current version是 3Expected version是 5待执行的是 004 和 005那就按顺序跑。用安全模式逐个执行openclaw --migrate --safe --only 004_add_index openclaw --migrate --safe --only 005_add_audit_table或者直接迁移到目标版本openclaw --migrate --safe --to 5--safe模式会做三件事事务包装、迁移前自动备份、出错即停。如果 004 失败005 不会执行数据库回滚到迁移前状态。如果迁移文件里有CREATE INDEX或ALTER TABLE ADD COLUMN这种可能重复执行的操作建议改成幂等写法。比如把CREATE INDEX改成CREATE INDEX IF NOT EXISTS把ALTER TABLE ADD COLUMN改成先检查列是否存在再添加。下面是一个修复迁移文件的示例module.exports { up: function(db) { const indexes db.exec( SELECT name FROM sqlite_master WHERE typeindex AND nameidx_users_email ); if (indexes.length 0) { db.exec(CREATE INDEX IF NOT EXISTS idx_users_email ON users(email)); console.log(索引已创建); } else { console.log(索引已存在跳过); } }, down: function(db) { db.exec(DROP INDEX IF EXISTS idx_users_email); } };第四步重跑迁移并验证。迁移执行完后再次检查状态openclaw --migration-status输出里Current version应该等于Expected versionPending migrations为空。然后启动 OpenClawopenclaw如果启动成功日志里没有 Schema 相关报错说明迁移链路已经通了。最后做一次数据完整性检查确认迁移没有丢数据import sqlite3 conn sqlite3.connect(.openclaw/data.db) cursor conn.cursor() cursor.execute(SELECT COUNT(*) FROM users) print(users 记录数:, cursor.fetchone()[0]) cursor.execute(SELECT COUNT(*) FROM _migrations) print(迁移记录数:, cursor.fetchone()[0]) conn.close()如果记录数和迁移前备份里的一致说明数据没丢。到这里migration failed和Schema version mismatch基本就解决了。4. 验证请求确认 OpenClaw 启动成功且模型接入正常迁移修好后光看启动日志还不够得实际发一个请求验证模型接入和数据库读写都正常。OpenClaw 启动后可以用内置的对话命令或者直接调 API。先确认服务在跑openclaw --status输出里应该显示Running数据库版本和代码版本一致。然后发一个测试对话openclaw chat --message 你好测试一下连接如果模型配置用的是 TaoToken请求会走https://taotoken.net/api。返回正常的话你会看到模型回复。如果报 401检查 API Key 是否正确如果报连接超时检查 Base URL 是否写成了https://taotoken.net/api不要多加斜杠或路径。也可以直接用 curl 测 TaoToken 的接口连通性curl -X POST https://taotoken.net/api/v1/chat/completions \ -H Authorization: Bearer sk-你的TaoToken密钥 \ -H Content-Type: application/json \ -d { model: claude-sonnet-4-20250514, messages: [{role: user, content: ping}], max_tokens: 10 }返回 JSON 里有choices字段就说明模型接入正常。如果返回401 Unauthorized去 TaoToken 控制台重新生成 Key地址是 https://taotoken.net/api-keys?utm_sourcetaotoken_aicg_blog_endutm_contentapi_keysutm_campaignrewrite 。如果返回model not found检查modelId是否和控制台里的一致。数据库这边验证迁移后的表结构openclaw --migration-status --format json输出 JSON 里pending数组应该为空current和expected相等。再跑一次openclaw --migration-history确认所有迁移都是Applied状态。如果 OpenClaw 有 Web 控制台打开后检查会话列表、项目列表是否能正常加载。这些数据都从 SQLite 读能加载说明 Schema 没问题。如果某个页面报no such column或no such table说明还有迁移没跑完回到第 3 步继续对齐。验证通过后建议把当前数据库再备份一次标记为“迁移后正常状态”cp .openclaw/data.db .openclaw/data.db.after_migration_ok这样下次再出问题可以直接回滚到这个已知正常的版本。5. 本篇常见错排查401、local proxy failed、reading choices、OAuth 报错对照迁移修完后启动时可能还会遇到其他报错。下面按真实报错逐条排查。401 Unauthorized模型接入的 API Key 无效或过期。检查.openclaw/config.json里的apiKey字段确认没有多余空格。如果用的是环境变量确认TAOTOKEN_API_KEY已导出。去 TaoToken 控制台重新生成 Key地址 https://taotoken.net/api-keys?utm_sourcetaotoken_aicg_blog_endutm_contentapi_keysutm_campaignrewrite 。生成后更新配置重启 OpenClaw。local proxy failed这个报错通常出现在配置了本地代理但代理没启动或者 Base URL 写成了localhost但端口不对。如果你没有用本地代理检查配置里是否有proxy字段删掉或注释。TaoToken 的 Base URL 是https://taotoken.net/api不需要额外代理。如果公司网络需要走代理确保代理地址和端口正确并且代理允许访问taotoken.net。reading choices这个报错说明请求发出去了但返回的 JSON 里没有choices字段。常见原因是模型 ID 写错或者 API 返回了错误信息但被当成正常响应解析。用 curl 直接测一下看返回体里是choices还是error。如果是error根据错误信息调整。比如model not found就换正确的modelIdinsufficient quota就去 TaoToken 控制台充值或检查额度。OAuth 报错如果 OpenClaw 配置了 OAuth 登录迁移后可能因为用户表结构变化导致 token 校验失败。检查users表里是否有oauth_token或refresh_token列。如果没有说明相关迁移没跑。用openclaw --migration-status确认待执行迁移补跑。如果 OAuth 配置在settings.json里确认oauth.clientId和oauth.clientSecret没被迁移覆盖。Migration lock held by another process迁移锁没释放。先确认没有其他 OpenClaw 进程在跑ps aux | grep openclaw如果有停掉。然后手动清理锁文件通常在.openclaw/migration.lockrm -f .openclaw/migration.lock再重跑迁移。如果用的是 Redis 锁去 Redis 里删掉对应的 key。Column already exists迁移文件重复执行。把迁移文件里的ALTER TABLE ADD COLUMN改成先检查再添加或者用openclaw --migration-mark标记为已应用跳过。参考第 3 步的幂等写法。Migration conflict: depends on ... but ... has not been applied迁移依赖顺序错了。检查迁移文件名前缀确保被依赖的迁移编号更小。比如004_add_index依赖003_add_column那 003 必须先跑。如果顺序没问题但 003 没跑成功先单独跑 003openclaw --migrate --safe --only 003_add_column然后再跑 004。排查时记住一个原则先看--migration-status和--migration-history确认版本和状态再看数据库实际表结构确认历史表和真实情况是否一致最后才动迁移文件。不要一上来就--force那会跳过冲突检查可能导致数据不一致。6. 长期稳定把迁移策略固化到 settings 里问题解决后把迁移策略写进配置避免下次升级再踩坑。核心是三条生产环境禁止自动迁移、迁移前强制备份、多节点用分布式锁。生产环境的migration配置{ migration: { autoMigrate: false, safeMode: true, backupBeforeMigrate: true, transactionPerMigration: true, stopOnError: true, allowDrop: false, requireApproval: true, lockTimeout: 60000, retryOnLock: true, maxRetries: 3, validateChecksums: true, immutableApplied: true } }allowDrop设为false禁止迁移里执行DROP TABLE或DROP COLUMN。requireApproval设为true迁移前需要人工确认。immutableApplied设为true已应用的迁移文件不允许修改改了会被校验和检查拒绝。多节点环境加分布式锁{ migration: { clusterLock: { enabled: true, backend: redis, lockKey: openclaw:migration:lock, lockTTL: 600000, retryCount: 30, retryDelay: 2000 } } }这样多个节点同时启动时只有一个能拿到锁执行迁移其他节点等待。锁 TTL 设 10 分钟防止死锁。CI/CD 里的迁移步骤steps: - name: Backup database run: openclaw --backup --before-migration - name: Run migrations run: | openclaw --migrate --safe --stop-on-error openclaw --migration-status - name: Verify migrations run: | STATUS$(openclaw --migration-status --format json) PENDING$(echo $STATUS | jq .pending | length) if [ $PENDING -gt 0 ]; then echo 有未执行的迁移 exit 1 fi - name: Rollback on failure if: failure() run: openclaw --restore --latest-backup迁移文件纳入 Git 管理命名用001_、002_前缀保证顺序。已应用的迁移不要改需要变更就新建一个迁移文件。每次迁移后跑一次openclaw --migration-audit记录执行时间、执行者、变更内容方便回溯。最后把 TaoToken 的接入配置和迁移配置分开管理。模型接入用环境变量存 Key迁移配置用settings.json固化策略。这样升级 OpenClaw 时模型接入不受影响迁移也有安全网。下次再看到Schema version mismatch按第 3 步的备份、对齐、重跑流程走一遍基本十分钟内能恢复。