Skip to content

Fix PreparedModelCatalogConfigReplacedError on replies with OpenClaw >= 2026.9.x - #307

Open
cuijianzhuang wants to merge 8 commits into
Tencent:mainfrom
cuijianzhuang:main
Open

cuijianzhuang wants to merge 8 commits into
Tencent:mainfrom
cuijianzhuang:main

Conversation

@cuijianzhuang

Copy link
Copy Markdown

Summary

Fixes an issue where replies fail with PreparedModelCatalogConfigReplacedError on OpenClaw hosts >= 2026.9.x by resolving the host's current runtime config for each inbound message instead of reusing a stale startup snapshot.

Problem

The monitor loop was pinning the ctx.cfg snapshot handed to gateway.startAccount and reusing it for every reply. Newer hosts (>= 2026.9.x) republish the config object on each config write or reload and reject calls whose config no longer matches the published prepared-model-catalog owner. This caused:

  • Inbound messages to be received successfully
  • Every reply to fail with PreparedModelCatalogConfigReplacedError
  • The account to remain stuck with a stale config indefinitely (since it only restarts on channels.openclaw-weixin.* changes)

Solution

  • New module src/config/live-config.ts: Introduces createLiveConfigResolver() which wraps the host's selectApplicableRuntimeConfig rule to resolve the current runtime config per message. This rule:

    • Follows the republished config when the retained object is the host's
    • Preserves scoped configs when they differ from the host's
    • Re-evaluates on every call rather than caching the decision
  • Updated MonitorWeixinOpts: Changed config parameter to getConfig: LiveConfigResolver to resolve config per message instead of at startup

  • Updated monitorWeixinProvider: Calls getConfig() when processing each message instead of using a captured snapshot

  • Updated weixinPlugin: Creates a LiveConfigResolver from the startup config and passes it to the monitor

Implementation Details

  • Uses the host's own selectApplicableRuntimeConfig from openclaw/plugin-sdk/runtime-config-snapshot (available since declared minimum version 2026.5.12)
  • No host version requirement changes needed
  • Comprehensive test coverage in src/config/live-config.test.ts covering startup config, republished configs, scoped configs, and gateway reloads

cuijianzhuang and others added 8 commits September 12, 2026 17:40
The monitor loop captured the `ctx.cfg` snapshot handed to
`gateway.startAccount` and reused it for every reply. OpenClaw >= 2026.9.x
republishes the config object on each config write or reload and rejects
calls whose config no longer matches the published prepared-model-catalog
owner, so inbound messages were received while every reply failed with
`PreparedModelCatalogConfigReplacedError`. The account is only restarted for
`channels.openclaw-weixin.*` changes, so edits elsewhere left us holding a
superseded object indefinitely.

Each inbound message now re-reads the host's current runtime config, using
the same accessors the bundled channels use: `createRuntimeConfigReader`
first, then `selectApplicableRuntimeConfig`, then
`getRuntimeConfigSnapshot`/`getRuntimeConfig`. They are looked up through a
dynamic import so hosts down to the declared peer minimum (2026.5.12), which
publish neither module nor export, keep loading and simply fall back to the
startup snapshot.

`scripts/hotfix-live-config.mjs` applies the equivalent patch to an already
installed build (backup written, `--revert` restores it) for users who cannot
wait for a release.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NAnMV1vUceCgm9TnVRmmtW
Replace the runtime probe with a static, typed dependency on the host's own
resolution rule. `selectApplicableRuntimeConfig` — what
`createRuntimeConfigReader` is built on, and what the bundled channels resolve
through — exists in `openclaw/plugin-sdk/runtime-config-snapshot` from the
declared host minimum (2026.5.12) onwards, verified against the published
2026.5.12, 2026.8.1 and 2026.9.4 type surfaces, so nothing needs probing and
no host requirement changes.

`createLiveConfigResolver` is now synchronous and evaluates the rule per call
rather than caching the decision, which keeps it correct across gateway
reloads. `MonitorWeixinOpts.config` is replaced by the required `getConfig`
resolver so no caller can pass a snapshot that goes stale.

The hotfix script for already installed builds applies the same rule, and now
reports "no patch needed" on builds that already carry the fix.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NAnMV1vUceCgm9TnVRmmtW
The shipped fix in src/config/live-config.ts is the supported path; keep only
that. Removes scripts/hotfix-live-config.mjs and its changelog mentions.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NAnMV1vUceCgm9TnVRmmtW
Fix PreparedModelCatalogConfigReplacedError on replies with OpenClaw >= 2026.9.x
createLiveConfigResolver re-evaluated selectApplicableRuntimeConfig on every
call against the startup ctx.cfg. The host matches that object against its
current source snapshot, so after the first config write that changes content
the match fails and the resolver falls back to the stale startup config -
PreparedModelCatalogConfigReplacedError returns on every reply.

Mirror the host's createRuntimeConfigReader instead: decide once whether the
startup config is the host's, then always follow the current runtime
snapshot (keeping scoped configs). Use the host reader when exported; hosts
back to 2026.5.12 get an equivalent built on selectApplicableRuntimeConfig.

Tests now model real config writes (new source object) and cover both host
variants; the previous tests passed only because the mock never replaced the
source snapshot.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…-decision

fix: latch the live-config follow decision at account start
npm run ci failed at format:check on the destructuring added in the previous
commit. No behaviour change.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
style: format live-config.ts with oxfmt
@yuzheng14

Copy link
Copy Markdown
Contributor

Thanks for working on the live-config side of this. We tested the current head (dcef18e) on OpenClaw 2026.9.4 and still reproduced PreparedModelCatalogConfigReplacedError during inbound reply dispatch.

The remaining gap is that Weixin calls the legacy low-level dispatchReplyFromConfig path without usePublishedModelRuntime: true; the modern inbound pipeline supplies that option automatically. I opened #313 as an independent, complementary patch with a regression test and live end-to-end verification.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants