Files
DevFlow/docs/09-问题排查/aichat-apikey-401排查-2026-06-15.md
绝尘 04032a2a8d 重构: 文档汇总+进度看板+孤儿任务清理脚本+gitignore 噪音排除
- docs/02 架构设计: 新增 aichat审查/异步审批构想/流式渲染调研/generating状态机/密钥迁移健壮性/工作流脚本执行边界/条件表达式引擎/F-07 trait下沉/Agent架构说明/任务推进构想/功能创意池;更新功能决策记录+归档/对抗论证/文档记录规范/经验记录
- docs/03 模块文档: 新增 AI对话引擎/DAG引擎详解;更新 df-knowledge/df-nodes/df-storage/df-workflow/df-ai
- docs/05 代码审查: 新增 全栈审查/全局review/架构审查/近期改动审查/工作区多角度走查/自研memo流式渲染审查
- docs/09 问题排查: 新增 aichat-apikey-401
- docs/INDEX+README 索引同步;docs/todo 待办看板(2026-06-15 汇总)
- PROGRESS.md Sprint 22-25;URGENT.md 加急清单快照(5 项 P0 已全修)
- scripts/cleanup_orphan_tasks.{py,sh} 孤儿任务清理工具
- .gitignore 补 *.broken.bak + tmp/ 噪音排除
2026-06-15 05:14:21 +08:00

5.5 KiB
Raw Permalink Blame History

aichat API Key 鉴权失败排查

2026-06-15 排查 | 看板索引:docs/todo.md🔴 aichat API Key 401 排查」区块

现象

  • aichat 对话失败,提示「调用失败: API Key 无效或无权限」
  • 重新设置 apikey 后仍无效

错误来源

  • stream_recv.rs:107-113provider.stream(request) 直接返回 Err 分支
  • 错误文本:format!("AI 调用失败: {}", e)e 为服务端返回的 401/403
  • 即:请求发出去了,被服务端以鉴权/权限为由拒绝。非流式中断、非网络断连。

排查结论:代码链路全对(排除代码 bug

保存链路(正确)

  • commands.rs ai_save_provider:329-333api_key 非空 → set_provider_secret 写 OS keyring
    • 写失败会返回「密钥保存到系统钥匙串失败」(用户未报此错 → 写入成功
  • :334DB api_key 列恒空FR-S1 设计,真实密钥唯一源 = OS keyring

读取链路(正确)

所有 build_provider 调用点均经 resolve_provider_secretkeyring 优先fallback DB

调用点 行号 状态
agentic.rs对话主循环 :50
project.rs技术栈扫描 :378
knowledge_inject.rs知识提炼/嵌入) :33, :323
title.rs标题生成 :63
  • 例外:df-nodes/ai_node.rs:118p.api_key(工作流 AI 节点独立配置,不经 keyring与本问题无关

鉴权头(正确)

  • openai_compatAuthorization: Bearer {api_key}
  • anthropic_compatx-api-key: {api_key} + anthropic-version

URL 智能拼接(正确,三规则容错)

  • openai_compat.chat_urlopenai_compat.rs:253-262
    • 已含 /chat/completions → 直接用
    • /v<数字> 结尾(如 /v1 /v4GLM 的 /api/paas/v4)→ 补 /chat/completions
    • 仅域名(api.openai.com / api.deepseek.com)→ 补 /v1/chat/completions
  • anthropic_compat.messages_urlanthropic_compat.rs:255-264
    • 已含 /v1/messages → 直接用
    • /v1 结尾 → 补 /messages
    • 否则(.../api/anthropicapi.anthropic.com)→ 补 /v1/messages

链路已全部验证无误。401 来自服务端,根因不在 devflow 代码。

根因方向(服务端 401按概率排序

1. API Key 本身无效(最常见)

  • 过期 / 欠费 / 额度用尽
  • 粘贴时带空格、引号、回车或多余字符
  • 中转 key 与官方 key 混淆(不同渠道 key 不通用)

2. provider_type 与端点不匹配

  • openai_compatBeareranthropic_compatx-api-key,两者鉴权头互斥
  • 若实际是 OpenAI 兼容端点却选 anthropic_compat(或反之)→ 鉴权头不被识别 → 401
  • 典型:
    • GLM 官方 chat 端点(open.bigmodel.cn/api/paas)→ openai_compat
    • GLM 的 Anthropic 兼容端点(open.bigmodel.cn/api/anthropic)→ anthropic_compat
    • Claude 官方(api.anthropic.com)→ anthropic_compat
    • 各类中转/聚合站one-api/new-api→ 一般 openai_compat

3. base_url 填错

  • 多/少 /v1 段(智能拼接已容错多数情况,但中转站路径各异仍可能错)
  • 协议错(http vs https
  • 域名拼写错误

4. model 名错/无权限

  • 部分 API 网关对无效 model 返回 401 而非 404
  • model 名大小写、版本号、前缀错(如 glm-4 vs glm-4-flashclaude-3-5-sonnet vs claude-3-5-sonnet-20241022

验证步骤(直连测试,区分 key / 配置)

在终端用 curl 直连,绕开 devflow精准定位是 key 还是配置问题:

A. provider_type = openai_compatBearer 鉴权)

curl -s "{base_url}/v1/chat/completions" \
  -H "Authorization: Bearer {KEY}" \
  -H "Content-Type: application/json" \
  -d '{"model":"{MODEL}","messages":[{"role":"user","content":"hi"}],"max_tokens":5}'

B. provider_type = anthropic_compatx-api-key 鉴权)

curl -s "{base_url}/v1/messages" \
  -H "x-api-key: {KEY}" \
  -H "anthropic-version: 2023-06-01" \
  -H "Content-Type: application/json" \
  -d '{"model":"{MODEL}","max_tokens":5,"messages":[{"role":"user","content":"hi"}]}'

{base_url} {KEY} {MODEL} 替换为设置页实际填的值base_url 按拼接规则:不带末尾的 /v1/...,但带了也兼容)。

结果判断

返回 含义
正常 completion JSON key+url+type+model 全对 → 问题在 devflow 内部(概率极低,链路已验证;此时查 Rust 日志看实际请求)
401 / authentication_error key 无效, provider_type 选错(鉴权头不匹配)
404 base_url 拼接错,端点不存在
400 model 相关 model 名错/无权限
连接失败/超时 base_url 域名或网络问题

TODO

  • S-260615-01 用户执行直连测试A 或 B确认是 key、provider_type、base_url、model 哪一项的问题
  • 用户:核对 provider_type 与实际端点匹配(见「根因方向 2」的典型对照
  • 用户:核对 model 名拼写(对照 API 提供商文档)
  • 若直连测试通过、devflow 内仍 401开 Rust tracing 日志,查实际发出的 url + header 是否与直连一致(排除 body 字段触发的鉴权问题)
  • B-260615-01可选增强 stream_recv.rs:107 Err 分支增加诊断信息:把 provider_type + 实际请求 url + HTTP 状态码记入日志/错误返回,便于 401 快速定位(当前错误只透传服务端文本,看不出打了哪个 url