What happened / 发生了什么
在提供商源(Provider Source)管理页面,新建一个源后不保存、直接删除,会同时出现两个异常:
- Toast 报错:
Request failed with code 400
- 右侧面板残留:左侧列表中该源已消失,但右侧仍渲染着该源的配置表单
Reproduce / 如何复现?
- 进入 配置 → 提供商 页面,选中一个提供商类型(如
openai_chat_completion)
- 点击左侧面板的「添加提供商源」,新建一个源
- 不做任何修改,不点保存
- 在左侧列表中,对刚新建的源点击「删除」并确认
- 观察:
- 顶部弹出红色 Toast:
Request failed with code 400
- 左侧列表中该源消失
- 右侧仍然显示该源的配置面板
AstrBot version, deployment method (e.g., Windows Docker Desktop deployment), provider used, and messaging platform used. / AstrBot 版本、部署方式(如 Windows Docker Desktop 部署)、使用的提供商、使用的消息平台适配器
4.26.5 Windows Desktop
OS
Windows
Logs / 报错日志

新建一个Provider

然后立刻点击删除

报错400,且没有得到正确清理
场景验证
| 场景 |
结果 |
| 新建 → 输入 API key → 保存 → 删除 |
✅ 正常,右侧面板消失 |
| 新建 → 不动 → 直接删除 |
❌ 400 错误 + 右侧残留 |
| 上述 Bug 发生后 → 在残留面板输入 API key → 点保存 |
源在后端被创建,左侧面板重新出现,无报错 |
根因分析
useProviderSources.ts — addProviderSource()(第 424 行)
新建源时仅操作本地状态,不调 API 持久化:
function addProviderSource(templateKey: string) {
const newSource = { ... }
providerSources.value.push(newSource) // 只加到本地数组
selectedProviderSource.value = newSource // 选中新源 → 右侧面板渲染
isSourceModified.value = true // 标记已修改
// ⚠️ 未调用 API,后端不知道有这个源
}
useProviderSources.ts — deleteProviderSource()(第 451 行)
删除时假设源一定在后端存在,API 调用和本地清理耦合在同一个 try 块:
async function deleteProviderSource(source) {
try {
await providerApi.deleteSource(source.id) // ① API 调用 — 400 抛出
// ⬇ 以下全部跳过 ⬇
providers.value = providers.value.filter(...) // ② 未执行
providerSources.value = providerSources.value.filter(...) // ③ 未执行
if (selectedProviderSource.value?.id === source.id) {
selectedProviderSource.value = null // ④ 未执行!核心问题
}
showMessage('删除成功')
} catch (error) {
showMessage(error.message, 'error') // ⑤ 仅显示错误
} finally {
await loadConfig() // ⑥ 重新拉取数据
}
}
执行顺序:
deleteProviderSource() 被调用
→ providerApi.deleteSource(source.id) → 后端查不到该源 → ValueError → HTTP 400
→ 进入 catch:弹出「Request failed with code 400」
→ 进入 finally:loadConfig() → 从后端拉 provider_sources → 该源不在其中
→ providerSources.value 被覆盖为后端数据 → 左侧列表正确(无该源)
→ 但 selectedProviderSource 仍指向已被"删除"的幽灵对象 → v-if="selectedProviderSource" 为 true
→ 右侧面板残留显示
后端 config_service.py:1357-1361 也确认了:删除不存在的源会抛 ValueError:
async def delete_provider_source(self, source_id: str) -> None:
sources = self.config.get("provider_sources", [])
next_sources = [source for source in sources if source.get("id") != source_id]
if len(next_sources) == len(sources):
raise ValueError(f"Provider source {source_id} not found") # → 400
触发条件总结
| 条件 |
说明 |
| 时机 |
addProviderSource() 之后、saveProviderSource() 之前 |
| 判断标志 |
isSourceModified.value === true — 源仅在本地,未持久化 |
影响范围
- 用户体验:错误提示让用户以为操作失败,但实际上源确实从列表中消失了,右侧残留的配置面板造成混淆
- 数据一致性:
selectedProviderSource 成为悬空引用,后续操作(如保存)可能意外复活该源
- 频率:新建源后立即后悔想删除,是自然的操作路径,不是边缘场景
修复建议
方案 A(最小改动):在 catch 中补上本地清理
catch (error: any) {
showMessage(error.message || '删除失败', 'error')
// 即使 API 失败,也清理本地状态
if (selectedProviderSource.value?.id === source.id) {
selectedProviderSource.value = null
selectedProviderSourceOriginalId.value = null
editableProviderSource.value = null
}
}
方案 B(推荐):判断源是否已持久化,未持久化的直接本地清理
async function deleteProviderSource(source: any) {
const confirmed = await askForConfirmation(...)
if (!confirmed) return
// 如果源从未保存到后端,直接清理本地状态
if (isSourceModified.value && selectedProviderSource.value?.id === source.id) {
providerSources.value = providerSources.value.filter(s => s.id !== source.id)
selectedProviderSource.value = null
selectedProviderSourceOriginalId.value = null
editableProviderSource.value = null
isSourceModified.value = false
showMessage('已取消新建')
return
}
try {
await providerApi.deleteSource(source.id)
// ... 现有清理逻辑
} catch (error) { ... }
}
推荐方案 B,因为它从根源避免了不必要的 API 调用,且语义更清晰:新建未保存 = 取消,删除已保存 = 删后端。
Are you willing to submit a PR? / 你愿意提交 PR 吗?
Code of Conduct
What happened / 发生了什么
在提供商源(Provider Source)管理页面,新建一个源后不保存、直接删除,会同时出现两个异常:
Request failed with code 400Reproduce / 如何复现?
openai_chat_completion)Request failed with code 400AstrBot version, deployment method (e.g., Windows Docker Desktop deployment), provider used, and messaging platform used. / AstrBot 版本、部署方式(如 Windows Docker Desktop 部署)、使用的提供商、使用的消息平台适配器
4.26.5 Windows Desktop
OS
Windows
Logs / 报错日志
场景验证
根因分析
useProviderSources.ts—addProviderSource()(第 424 行)新建源时仅操作本地状态,不调 API 持久化:
useProviderSources.ts—deleteProviderSource()(第 451 行)删除时假设源一定在后端存在,API 调用和本地清理耦合在同一个
try块:执行顺序:
后端
config_service.py:1357-1361也确认了:删除不存在的源会抛ValueError:触发条件总结
addProviderSource()之后、saveProviderSource()之前isSourceModified.value === true— 源仅在本地,未持久化影响范围
selectedProviderSource成为悬空引用,后续操作(如保存)可能意外复活该源修复建议
方案 A(最小改动):在
catch中补上本地清理方案 B(推荐):判断源是否已持久化,未持久化的直接本地清理
推荐方案 B,因为它从根源避免了不必要的 API 调用,且语义更清晰:新建未保存 = 取消,删除已保存 = 删后端。
Are you willing to submit a PR? / 你愿意提交 PR 吗?
Code of Conduct