fix(startup): keep the app bootable when config is partially broken

This commit is contained in:
zhayujie
2026-08-12 10:10:37 +08:00
parent 289d9739fc
commit b8bd9b459a
3 changed files with 58 additions and 2 deletions
+27 -1
View File
@@ -280,6 +280,8 @@ def get_agent_registry() -> AgentRegistry:
"""
global _registry_instance, _registry_signature
import os
from config import conf
settings = conf()
@@ -288,7 +290,31 @@ def get_agent_registry() -> AgentRegistry:
if _registry_pinned and _registry_instance is not None:
return _registry_instance
if _registry_instance is None or _registry_signature != signature:
_registry_instance = AgentRegistry.from_config(settings)
try:
_registry_instance = AgentRegistry.from_config(settings)
except AgentRegistryError:
# An invalid `agents`/`default_agent_id` block would otherwise
# bubble all the way up through load_config() and take the whole
# process down before the web console can bind — leaving a
# desktop user with no UI to fix the very config that is broken.
# Fall back to the default single agent so the app still starts;
# the console can then edit the bad block. Source deployments
# keep failing loudly so the developer sees the error at once.
if os.environ.get("COW_DESKTOP") != "1":
raise
from common.log import logger
logger.error(
"[AgentRegistry] invalid 'agents' config; ignoring it and "
"starting with the default agent. Fix it in the console.",
exc_info=True,
)
fallback = {
k: v
for k, v in dict(settings).items()
if k not in ("agents", "default_agent_id")
}
_registry_instance = AgentRegistry.from_config(fallback)
_registry_signature = signature
return _registry_instance
+12 -1
View File
@@ -82,7 +82,18 @@ class ChannelManager:
with self._lock:
channels = []
for name in channel_names:
ch = channel_factory.create_channel(name)
# One misconfigured channel (e.g. wechatcom_app without its
# corp_id/token/aes_key) must not take the whole process down:
# instantiating it can raise while parsing config. The web
# console in particular has to come up so the desktop shell can
# surface the error and let the user fix the config. Skip the
# broken channel and keep the rest.
try:
ch = channel_factory.create_channel(name)
except Exception as e:
logger.error(f"[ChannelManager] Failed to create channel '{name}', skipping it: {e}")
logger.exception(e)
continue
ch.cloud_mode = self.cloud_mode
self._channels[name] = ch
channels.append((name, ch))
+19
View File
@@ -40,6 +40,25 @@ class WechatComAppChannel(ChatChannel):
logger.info(
"[wechatcom] Initializing WeCom app channel, corp_id: {}, agent_id: {}".format(self.corp_id, self.agent_id)
)
# Fail fast with a readable message when the channel is enabled but its
# required credentials are missing. Otherwise WeChatCrypto concatenates
# a None aes_key with a string and raises an opaque
# "unsupported operand type(s) for +: 'NoneType' and 'str'".
missing = [
key
for key, val in (
("wechatcom_corp_id", self.corp_id),
("wechatcomapp_token", self.token),
("wechatcomapp_aes_key", self.aes_key),
)
if not val
]
if missing:
raise RuntimeError(
"[wechatcom] WeCom app channel is enabled but missing required config: "
+ ", ".join(missing)
+ ". Fill them in config.json or remove 'wechatcom_app' from channel_type."
)
self.crypto = WeChatCrypto(self.token, self.aes_key, self.corp_id)
self.client = WechatComAppClient(self.corp_id, self.secret)