What happened / 发生了什么
首先这个 issue 是 AI 帮我写的,因为我对发生了什么也摸不着头脑
AstrBot 对插件 _conf_schema.json 中 "type": "dict" 的配置项存在数据丢失缺陷:用户在 WebUI(ObjectEditor 键值对编辑器)保存的键值对,在插件重载/重启后全部丢失,配置文件里该键被重置为空对象 {}。
根因在 astrbot/core/config/astrbot_config.py 的 check_config_integrity():
-
插件配置由 star_manager.py 以 schema 构造 AstrBotConfig(..., schema=_conf_schema),__init__ 里 default_config = self._config_schema_to_default_config(schema)——dict 类型键的参考默认值是 {}
-
每次加载执行 check_config_integrity(default_config, conf),对参考值为 dict 的键递归进入:
elif isinstance(value, dict):
if not isinstance(conf[key], dict):
new_conf[key] = value
has_new = True
else:
child_has_new = self.check_config_integrity(value, conf[key], ...)
new_conf[key] = conf[key]
has_new |= child_has_new
-
递归层 refer_conf = {},用户添加的键被视为"参考配置中没有的配置项"(仅打印 Config key removed),最终:
conf.clear()
conf.update(new_conf) # new_conf 为空 → 用户内容被清空
-
has_new=True 时 __init__ 还会 save_config() 把清空结果写回磁盘
即:任何插件使用 dict 类型存用户动态键值对,内容必然在加载/保存时被抹掉。前端 ObjectEditor 明确支持 dict 编辑("修改键值对"弹窗、JSON 值类型校验),与后端行为矛盾。
核心配置不受影响的原因:核心的用户可编辑 dict(如 custom_headers)位于 provider_sources(list 类型)的元素内部,check_config_integrity 不递归 list,整包保留——这恰好提供了可用的规避模式。
Reproduce / 如何复现?
最小复现(逻辑与部署版 4.27.0 逐行一致):
def check_config_integrity(refer_conf, conf, path=""):
has_new = False
new_conf = {}
for key, value in refer_conf.items():
if key not in conf:
new_conf[key] = value; has_new = True
elif conf[key] is None:
new_conf[key] = value; has_new = True
elif isinstance(value, dict):
if not isinstance(conf[key], dict):
new_conf[key] = value; has_new = True
else:
child = check_config_integrity(value, conf[key], path + "." + key if path else key)
new_conf[key] = conf[key]; has_new |= child
else:
new_conf[key] = conf[key]
for key in list(conf.keys()):
if key not in refer_conf:
print("Config key removed:", path + "." + key if path else key); has_new = True
conf.clear(); conf.update(new_conf)
return has_new
# 插件 _conf_schema.json: {"custom": {"type": "dict", "default": {}}}
refer = {"custom": {}} # schema 生成的参考默认值
conf = {"custom": {"好友": ["umo1"]}} # 用户在 WebUI 保存的内容
check_config_integrity(refer, conf)
print(conf) # → {'custom': {}} ← 内容被清空
复现步骤:
- 插件
_conf_schema.json 定义 "my_config": {"type": "dict", "default": {}}
- WebUI 插件配置中向
my_config 添加键值对并保存(保存瞬间文件里是有内容的)
- 重载插件或重启 AstrBot
- 配置文件中
my_config 变为 {},WebUI 里内容消失,日志出现 Config key removed: <插件名>.my_config.<键名>
对照实验:若把同样内容放在 list 类型键的列表元素内部(核心 provider_sources 的写法),则不会丢失——证明问题只出在 dict 类型的递归清理。
AstrBot version, deployment method (e.g., Windows Docker Desktop deployment), provider used, and messaging platform used. / AstrBot 版本、部署方式(如 Windows Docker Desktop 部署)、使用的提供商、使用的消息平台适配器
OS
Windows
Logs / 报错日志
完整 Debug 日志(360 行窗口,含插件更新、配置迁移、两次重载清除"汐月"组的全过程)见 gist:
https://gist.github.com/Galaxy1108/3e42f45c233af514c28cab180d391f3f
因为 log 太长了....
关键片段(插件每次重载时重复出现):
[2026-08-02 21:22:44.117] [Core] [INFO] [star.star_manager:1123]: Loading plugin astrbot_plugin_shared_context ...
[2026-08-02 21:22:44.147] [Core] [INFO] [config.astrbot_config:215]: Config key removed: custom_groups.share_groups.汐月
[2026-08-02 21:22:44.147] [Core] [INFO] [config.astrbot_config:221]: Config key order fixed: custom_groups.share_groups
[2026-08-02 21:23:34.243] [Core] [INFO] [star.star_manager:1123]: Loading plugin astrbot_plugin_shared_context ...
[2026-08-02 21:23:34.248] [Core] [INFO] [config.astrbot_config:215]: Config key removed: custom_groups.share_groups.汐月
[2026-08-02 21:23:34.248] [Core] [INFO] [config.astrbot_config:221]: Config key order fixed: custom_groups.share_groups
其中 Config key removed: custom_groups.share_groups.汐月 即用户在 WebUI 保存的组"汐月"被 check_config_integrity 清除,每次重载都会重复发生。
用户保存后、重载前的配置文件内容:
{
"custom_groups": {
"enable_custom_groups": true,
"cross_bot_share": true,
"share_groups": { "汐月": ["qq-bot:FriendMessage:xxx"] }
}
}
重载后被写回:
{
"custom_groups": {
"enable_custom_groups": true,
"cross_bot_share": true,
"share_groups": {}
}
}
Are you willing to submit a PR? / 你愿意提交 PR 吗?
Code of Conduct
What happened / 发生了什么
AstrBot 对插件
_conf_schema.json中"type": "dict"的配置项存在数据丢失缺陷:用户在 WebUI(ObjectEditor 键值对编辑器)保存的键值对,在插件重载/重启后全部丢失,配置文件里该键被重置为空对象{}。根因在
astrbot/core/config/astrbot_config.py的check_config_integrity():插件配置由
star_manager.py以 schema 构造AstrBotConfig(..., schema=_conf_schema),__init__里default_config = self._config_schema_to_default_config(schema)——dict 类型键的参考默认值是{}每次加载执行
check_config_integrity(default_config, conf),对参考值为 dict 的键递归进入:递归层 refer_conf =
{},用户添加的键被视为"参考配置中没有的配置项"(仅打印Config key removed),最终:has_new=True时__init__还会save_config()把清空结果写回磁盘即:任何插件使用
dict类型存用户动态键值对,内容必然在加载/保存时被抹掉。前端 ObjectEditor 明确支持 dict 编辑("修改键值对"弹窗、JSON 值类型校验),与后端行为矛盾。核心配置不受影响的原因:核心的用户可编辑 dict(如
custom_headers)位于provider_sources(list 类型)的元素内部,check_config_integrity不递归 list,整包保留——这恰好提供了可用的规避模式。Reproduce / 如何复现?
最小复现(逻辑与部署版 4.27.0 逐行一致):
复现步骤:
_conf_schema.json定义"my_config": {"type": "dict", "default": {}}my_config添加键值对并保存(保存瞬间文件里是有内容的)my_config变为{},WebUI 里内容消失,日志出现Config key removed: <插件名>.my_config.<键名>对照实验:若把同样内容放在 list 类型键的列表元素内部(核心
provider_sources的写法),则不会丢失——证明问题只出在 dict 类型的递归清理。AstrBot version, deployment method (e.g., Windows Docker Desktop deployment), provider used, and messaging platform used. / AstrBot 版本、部署方式(如 Windows Docker Desktop 部署)、使用的提供商、使用的消息平台适配器
OS
Windows
Logs / 报错日志
完整 Debug 日志(360 行窗口,含插件更新、配置迁移、两次重载清除"汐月"组的全过程)见 gist:
https://gist.github.com/Galaxy1108/3e42f45c233af514c28cab180d391f3f
关键片段(插件每次重载时重复出现):
其中
Config key removed: custom_groups.share_groups.汐月即用户在 WebUI 保存的组"汐月"被check_config_integrity清除,每次重载都会重复发生。用户保存后、重载前的配置文件内容:
{ "custom_groups": { "enable_custom_groups": true, "cross_bot_share": true, "share_groups": { "汐月": ["qq-bot:FriendMessage:xxx"] } } }重载后被写回:
{ "custom_groups": { "enable_custom_groups": true, "cross_bot_share": true, "share_groups": {} } }Are you willing to submit a PR? / 你愿意提交 PR 吗?
Code of Conduct