创建时间:2026-04-15 关联:
issue-tracker.mdB-001、SentryNo provider credentials available(1,462 events / 14d,Top 2) Issues:#461、#478、#476、#457、#470
| # | 用户 | 平台 | 关键陈述 |
|---|---|---|---|
| 461 | patgdut | macOS | "0.46.0 以后的版本都无法使用最新的 Claude Code",回退到 0.45 正常 |
| 461 | Theo-jobs | macOS | "本地通过 cc-switch 配置的,之前是可以的;走 API 可以,CLI 不行" |
| 461 | patgdut | — | "这个检测是不准的,你实际发一句 Who are you 是可以通的" |
| 478 | patgdut | — | "使用 cc-switch 后一直报错无法使用" |
| 476 | — | Windows | "配置 openrouter 一直提示不通过,cc-switch 切换后本地 claude 可以使用" |
| 466 | patgdut | — | 根因分析:"打包版每次启动随机端口 → localStorage 清空"——另一类已知问题 |
共同特征:
- 都用 cc-switch 作为 Claude Code CLI 的凭据管理器
- CodePilot 侧没有在 UI 里配 provider(或配了也不是 cc-switch 那一套)
- 终端直接跑
claude可用 - 在 CodePilot 里发消息报 "No provider credentials available"
查 /Users/op7418/Documents/code/资料/cc-switch-main:
写入点 A:~/.claude/settings.json 的 env 块
src-tauri/src/config.rs:73-86 → get_claude_settings_path() 返回 ~/.claude/settings.json(legacy 兼容 claude.json)。
src-tauri/src/provider.rs:493-502:
let settings_config = serde_json::json!({
"env": {
"ANTHROPIC_BASE_URL": self.base_url,
"ANTHROPIC_AUTH_TOKEN": self.api_key,
"ANTHROPIC_MODEL": model,
"ANTHROPIC_DEFAULT_HAIKU_MODEL": haiku,
"ANTHROPIC_DEFAULT_SONNET_MODEL": sonnet,
"ANTHROPIC_DEFAULT_OPUS_MODEL": opus,
}
});写入点 B:~/.claude/config.json 的 primaryApiKey: "any"
src-tauri/src/claude_plugin.rs:51-88 → 写入 {"primaryApiKey": "any"} 作为托管模式标记。
cc-switch 不设 shell env、不设 system env、不写 CodePilot DB。 一切都走 Claude Code CLI 的原生配置通道。
node_modules/@anthropic-ai/claude-agent-sdk/cli.js 实测:
settings.env 合并逻辑(压缩代码还原):
if (A.env && typeof A.env === "object") {
for (let [z, w] of Object.entries(A.env))
if (typeof w === "string" && w.length > 0) {
if (!rG6.has(z.toUpperCase())) K[z] = w;
}
}rG6 黑名单(67 项,完整列表在 node_modules/@anthropic-ai/claude-agent-sdk/cli.js 内)关键断言:
- ❌
ANTHROPIC_API_KEY— 不在黑名单,会被应用 - ❌
ANTHROPIC_AUTH_TOKEN— 不在黑名单,会被应用 - ❌
ANTHROPIC_BASE_URL— 不在黑名单,会被应用 - ✅
ANTHROPIC_MODEL— 在黑名单(被剥) - ✅
ANTHROPIC_DEFAULT_HAIKU_MODEL/ANTHROPIC_DEFAULT_SONNET_MODEL/ANTHROPIC_DEFAULT_OPUS_MODEL— 在黑名单(被剥) - ✅
CLAUDE_CODE_SUBAGENT_MODEL— 在黑名单(被剥)
结论:只要 SDK 的 settingSources 包含 'user',cc-switch 写的认证三项会被 SDK 应用到子进程 env;但模型名会被剥。
src/lib/runtime/registry.ts:50-81 — hasCredentialsForRequest()(auto mode 的路由决策依据):
function hasCredentialsForRequest(providerId?: string): boolean {
if (process.env.ANTHROPIC_API_KEY || process.env.ANTHROPIC_AUTH_TOKEN) return true;
if (getSetting('anthropic_auth_token')) return true; // CodePilot 自己的 DB
if (providerId === 'env') return false;
if (providerId && providerId !== 'env') {
const p = getProvider(providerId);
if (p?.api_key) return true;
// ...
}
for (const p of getAllProviders()) {
if (p.api_key) return true;
// ...
}
return false;
}检测源只有三个:
process.env.ANTHROPIC_*getSetting()读 CodePilot 自己的 DB(sdk_settings表)- CodePilot DB 的
providers表
完全没有读 ~/.claude/settings.json 的 env 块。
src/lib/runtime/registry.ts:92-132 — resolveRuntime():
// 3. Auto: prefer SDK only if CLI exists AND Anthropic credentials are available.
const sdk = getRuntime('claude-code-sdk');
if (sdk?.isAvailable() && hasCredentialsForRequest(providerId)) return sdk;
const native = getRuntime('native');
if (native?.isAvailable()) return native;cc-switch 用户在 auto 模式下:
sdk.isAvailable()= true(CLI 二进制存在)hasCredentialsForRequest()= false(看不到 settings.json)- → 走 native runtime
Native runtime 调 src/lib/ai-provider.ts:56 → createModel():
export function createModel(opts: CreateModelOptions = {}): CreateModelResult {
const resolved = resolveProvider({ ... });
if (!resolved.hasCredentials && !resolved.provider) {
throw new Error(
'No provider credentials available. Please configure a provider in Settings or set ANTHROPIC_API_KEY.',
);
}
...
}对应 Sentry Top 2 指纹:No provider credentials available. Please configure a provider in Settings or set ANTHROPIC_API_KEY. 1,462 events / 14d。
Native runtime 用 Vercel AI SDK 直连 API,完全不走 ~/.claude/settings.json。 即便手动强制 native,也无法用 cc-switch 的配置。
若用户在设置里把 agent_runtime 改成 claude-code-sdk(不是 auto):
resolveRuntime()line 110-112 显式分支 → 返回 SDK runtime- 走
claude-client.ts:498 streamClaudeSdk()路径 src/lib/provider-resolver.ts:653env 模式settingSources: ['user', 'project', 'local']- SDK 子进程加载
~/.claude/settings.json→ merge auth env → 实际可用
但绝大多数用户不会主动改这个设置,就看到"No provider credentials available"就走了。
src/lib/provider-resolver.ts:314-318:
// Prevent ~/.claude/settings.json from overriding CodePilot's provider configuration.
// When set, Claude Code CLI's withoutHostManagedProviderVars() strips all provider-routing
// variables from the user's settings file (see upstream managedEnv.ts / managedEnvConstants.ts).
env.CLAUDE_CODE_PROVIDER_MANAGED_BY_HOST = '1';grep CLAUDE_CODE_PROVIDER_MANAGED_BY_HOST node_modules/@anthropic-ai/claude-agent-sdk/cli.js → 0 hits。
SDK 0.2.62 里根本没有这个变量,也没有 withoutHostManagedProviderVars 函数。引用的 managedEnv.ts / managedEnvConstants.ts 也不存在于当前 SDK 版本。这是 死代码(可能来自更早的 SDK 版本或设计稿)。不主动引发 bug,但注释误导。
引入 commit:6324490 feat: provider governance Phase 1+2 — schema validation, authStyle fixes, host takeover (2026-04-04,v0.47.0 发布时)。
| 版本 | 日期 | 关键变更 | 影响 |
|---|---|---|---|
| v0.46.0 | 2026-04-04 00:04 | 89d3e97 Ollama 支持等 |
未直接涉及凭据链 |
| v0.47.0 | 2026-04-05 00:27 | 6324490 host takeover(引入 MANAGED_BY_HOST 死代码) |
无功能影响 |
| v0.48.0 | 2026-04-09 13:47 | fc61c69 auto mode checks Anthropic creds before preferring SDK (#456) |
引入 |
| v0.48.1-2 | 2026-04-11 | a4c72e4 hasCredentialsForRequest + 3 轮修复 |
问题锁死 |
真实转折点:v0.48.0。 用户说"0.46 以后"可能是版本记忆误差,或者他们在 0.46 升级到 0.48 后才稳定报错。
抓 #474 附件 codepilot-doctor-2026-04-13.json:
- 用户 provider ID:
b00e9f73e2b99e8e5be017de37cc3926,name: "Anthropic Third-party API" hasCredentials: true, authStyle: api_keyliveProbe: "Live probe timed out after 15s"- base URL:
http://model.mify.ai.srv/anthropic— 内部 DNS,外部不可达
#474 的根因是第三方内部 API 不可达 + 模型名未指定(doctor warn "no-explicit-model")。但 runtime logs 里 [agent-loop] Error: No provider credentials available 出现 3 次 —— 这说明 agent-loop 路径调用 createModel({}) 时传了空 opts,没带 providerId。是另一个独立 bug(runtime→createModel 的 providerId 透传丢失),不在本 issue 范围,单独跟进。
No provider credentials available. Please configure a provider in Settings or set ANTHROPIC_API_KEY.— 1,462 eventsClaude Code process exited with code 1— 7,501 events(包含 cc-switch 用户被迫走 CLI 失败的部分)AI_NoOutputGeneratedError— 1,279 events(部分由 SDK 子进程被错路由到 native 导致)
- 5 个 open issue 明确提到 cc-switch(#461/#478/#476/#457/#470 含相关评论)
- 多位用户明确建议"回退到 0.45/0.46"
- 社区信任受损:#466 用户建议"加内测版"
新建 src/lib/claude-settings.ts:
/**
* Read Anthropic credentials from ~/.claude/settings.json (or legacy claude.json).
* This file is managed by external tools like cc-switch, which don't set shell env.
* Returns null when the file is missing, unparseable, or has no credentials.
*/
export interface ClaudeSettingsCredentials {
apiKey?: string;
authToken?: string;
baseUrl?: string;
model?: string;
}
export function readClaudeSettingsCredentials(): ClaudeSettingsCredentials | null;改动点:
src/lib/runtime/registry.ts hasCredentialsForRequest():在现有检查开头加入 settings.json 读取src/lib/provider-resolver.ts buildResolution()env-mode 分支:envHasCredentials加入 settings.json 作为来源,settingSources保持['user','project','local']
效果: auto 模式对 cc-switch 用户返回 SDK runtime → SDK 子进程加载 settings.json → 子进程得到 ANTHROPIC_AUTH_TOKEN / BASE_URL → 正常工作。
src/lib/provider-resolver.ts:314-318 直接删除。注释换成简短说明"SDK 自行负责加载 settings.json,黑名单由 SDK 内部维护"。
src/lib/ai-provider.ts:66 当 ~/.claude/settings.json 存在凭据但 native runtime 被显式选择时,抛出更具体的错误:
"~/.claude/settings.json has credentials but Native runtime cannot read them.
Switch to Claude Code SDK runtime in Settings, or add the provider to CodePilot directly."
src/__tests__/unit/claude-settings-credentials.test.ts:
settings.json含env.ANTHROPIC_AUTH_TOKEN→hasCredentialsForRequest()返回 truesettings.json不存在 → 返回 false(不抛错)settings.json是 malformed JSON → 返回 false(不抛错)settings.json有env但无 auth key → 返回 false- env var 和 settings.json 都有 → 返回 true(不重复)
- legacy
claude.json兼容读取
前置: 清空 CodePilot 的所有 provider + 清空 shell env 的 ANTHROPIC_*。
复现步骤(修复前应失败):
- 安装 cc-switch,配一个第三方 Claude 中转(如 PackyCode / AICodeMirror)
- cc-switch 切换到该 provider,确认
~/.claude/settings.json写入 - 终端跑
claude,确认能用(发一句 "who are you") - 打开 CodePilot,启动新对话,发 "who are you"
- 预期旧版: "No provider credentials available..."
- 预期修复版: 正常返回回复
npm run test # 覆盖新增单测修复发布后 72h 观察:
No provider credentials available指纹 daily count 应下降- 不会出现新增的"cc-switch 相关"指纹
- 不做 在 UI 里显式"检测到 cc-switch 托管"的 banner(改动 1 已足够让功能可用;UI 提示可以后续补)
- 不做 model 名从 settings.json 恢复(SDK 黑名单挡住,且 CodePilot 已有 model 选择器,不是痛点)
- 不做
~/.claude/config.json的primaryApiKey: "any"检测(可选特性,后续可用于 UI 提示) - 不做 修复
#474的 agent-loop providerId 丢失(单独跟进)
- 2026-04-15 — 初次分析,确认根因在
hasCredentialsForRequest不读 settings.json;确认 SDK 的rG6黑名单不包含 auth 键,因此改动 1 即可让 cc-switch 用户恢复 - 2026-04-15 — 放弃 "让 native runtime 也读 settings.json" 方案:native runtime 用 Vercel AI SDK,和 CLI 的 settings.json 语义不同(没有 MCP / hooks / skills 配置),硬读会引入不一致行为;改为在 native runtime 失败时给清晰错误引导用户切 runtime
- 2026-04-15 — 决定把
CLAUDE_CODE_PROVIDER_MANAGED_BY_HOST设置删掉而非保留:当前 SDK 0.2.62 不识别此变量,未来 SDK 若真加入同名 flag 且语义一致,我们的代码逻辑已经改为信任 SDK 的黑名单机制,不需要再宣称"我们托管了" - 2026-04-15 review 后跟进 — code review 指出删了 MANAGED_BY_HOST 之后没有任何机制阻止 SDK 把
~/.claude/settings.json的 env 块应用到子进程:SDK 的qZq()在settingSources含'user'时会把 settings.env 写进 process.env,覆盖掉 CodePilot spawn 时注入的 provider auth。初次补丁:DB-backed provider 的settingSources改为['project', 'local'](不含 'user') - 2026-04-15 第二轮 review 推翻首轮补丁 — reviewer 正确指出代价评估错误:我原以为只失去
~/.claude/CLAUDE.md和 hooks,实际 SDK 的多个用户级能力都依赖settingSources: ['user']自动发现(user-level MCP、plugins、hooks、CLAUDE.md),dropping 'user' 会让显式配 DB provider 的所有用户都丢这些能力。回退 settingSources 改动。 - 2026-04-15 第三轮 review 提出"按 provider group 决定凭据归属"原则:
- env group(provider_id='env')→ 完全尊重 Claude Code 自己的来源(settings.json + cc-switch)
- DB provider 显式选中 → auth/baseURL/model 必须仅以 DB provider 的配置为准;settings.json 的 ANTHROPIC_* 不得覆盖
- 但 要保留 user-level 非认证配置(MCP/plugins/hooks/CLAUDE.md)
- auto/SDK/native 只决定 runtime 选择,不改变凭据归属语义
- 最终方案(实施):per-request shadow
~/.claude/- 新增
src/lib/claude-home-shadow.ts提供createShadowClaudeHome({ stripAuth }) - DB provider 路径下:建立临时目录,
.claude/settings.json是剥离ANTHROPIC_*keys 的副本(但保留 mcpServers / hooks / enabledPlugins / permissions / apiKeyHelper / 非认证 env entries 等所有非认证字段),其余~/.claude/<entry>通过 symlink(Unix)/ junction(Windows 目录)/ copy(Windows 文件 fallback)镜像 - SDK 子进程的 HOME / USERPROFILE 指向 shadow root → SDK 的
qZq()读到剥离版 settings.json → 不再覆盖 CodePilot 注入的 provider auth - env 模式 + 未指定 provider 模式:直接 pass-through 到真实 HOME(cc-switch 路径完全不变)
- cleanup 在 stream 的 finally 块里做,best-effort
runtime/registry.ts hasCredentialsForRequest()同步收紧:DB provider 路径只看 DB provider 自身的 api_key,不再用 settings.json 兜底(避免 "用户配错 Kimi 但被 cc-switch 静默救活" 的隐式 fallback)
- 新增
- 验收回归测试:
claude-home-shadow.test.ts(12 个 case)+claude-settings-credentials.test.ts新增 3 个 provider-group ownership case,覆盖:env+settings.json / DB+settings.json 共存 / DB 配错 key 不被救活 / shadow 保留 mcpServers + hooks + enabledPlugins + apiKeyHelper / shadow 保留 user-level skills/agents/commands/plugins 子目录 / cleanup 真实删除 - 2026-04-15 第五轮 review:project/local 层 bleed
- reviewer 指出 shadow HOME 只覆盖 user 层;
<cwd>/.claude/settings.json/<cwd>/.claude/settings.local.json的 env 块仍可越权(SDKqZq()对 user/project/local 三层都做process.env[K]=settings.env[K]) - 评估了"shadow workspace(cwd 镜像)":行不通——文件创建工具(Edit/Write)操作相对路径,新建文件会落在 shadow 而不是真实 cwd
- 改用 reviewer 建议的另一条路:"stop exposing those layers + 显式保留我们需要的非认证功能"
- DB provider settingSources 收紧为
['user'](drop 'project' + 'local') - 新增回归测试
provider-resolver-fixes.test.ts:DB provider settingSources ===['user'],且断言不含 'project'/'local'
- reviewer 指出 shadow HOME 只覆盖 user 层;
- 2026-04-15 第六轮 review:drop 'project' 把项目
.mcp.json也搂走了- reviewer 指出我先前对 mcp-loader contract 的理解有误:mcp-loader 用
process.cwd()读.mcp.json,而 Next.js server 的 cwd 不是用户项目目录,所以那条路径根本不准;真正生效的是 SDK 通过 'project' settingSource 自动加载<userCwd>/.mcp.json - drop 'project' 后,团队 check-in 的
.mcp.json项目级 MCP 也跟着丢了(这些是 auth-neutral 的,本不该被 trade-off 掉) - 修复:在
mcp-loader.ts新增loadProjectMcpServers(cwd)显式从指定 cwd 读.mcp.json并解析${...}占位符;claude-client.ts streamClaudeSdk在 DB provider 路径下调用并合并到queryOptions.mcpServers,让 SDK 通过显式 Options 拿到这些 MCPs - 名字冲突时优先级:CodePilot UI 管理 / placeholder 解析后的 servers > 项目
.mcp.json(用户当前选择层 > 团队默认层) - 仅在
streamClaudeSdk实施;generateTextViaSdk(cwd=os.homedir)和runLiveProbe(cwd=os.tmpdir)的 cwd 不是用户项目目录,无相关 .mcp.json - 新增回归测试
project-mcp-injection.test.ts(9 个 case):基本读取、无 .mcp.json 时返回 undefined、disabled servers 过滤、${...}占位符解析、缺失占位符 → 空字符串、malformed JSON 不抛错、缺失 mcpServers 字段、空对象返回 undefined
- reviewer 指出我先前对 mcp-loader contract 的理解有误:mcp-loader 用
- 2026-04-15 第七轮 review:
loadProjectMcpServers漏了mcpServerOverrides- reviewer 指出我新写的 helper 没有应用
~/.claude/settings.json的mcpServerOverrides——这是 CodePilot MCP Manager UI 持久化"我把这个 server 关了"或"我把这个 server 强制开"的位置(原始loadAndMerge是有这层处理的,见mcp-loader.ts:57-62) - 后果:DB-provider 会话可能会默默重新启用用户在 UI 里关掉的项目 MCP,或反过来——UI 状态和 SDK 实际加载之间出现分裂
- 修复:
loadProjectMcpServers也读~/.claude/settings.json的mcpServerOverrides,UI override 的优先级高于.mcp.json文件里的enabled字段(与loadAndMerge完全一致) - 新增 4 个回归 case:UI 关掉一个 → 真关;UI 开启一个 file 关的 → 真开;混合 override 只影响 named server;无 settings.json 不崩
- reviewer 指出我新写的 helper 没有应用
- 2026-04-15 第四轮 review 三个收尾点全修:
- [P1] shadow HOME 漏镜像
~/.claude.json:之前只镜像~/.claude/子树,但~/.claude.json(HOME 根级,存放用户级 MCP servers,见mcp-loader.ts:46)当 HOME 指向 shadow 时 SDK 看不到了 → 显式 DB provider 用户会静默丢掉.claude.json里的 MCP。修复:createShadowClaudeHome同步镜像~/.claude.json(也走stripAuthEnv),settingsJsonHasAuthOverride()检测两个文件,shadow 触发条件改为"任意一个文件有 auth env" - [P2]
generateTextViaSdk()之前没接入 shadow HOME:被context-compressor、cli-tools/[id]/describe等辅助路径调用,绕过了 provider-group ownership → cc-switch 仍会污染辅助请求。修复:抽出共享 helperprepareSdkSubprocessEnv()(src/lib/sdk-subprocess-env.ts),generateTextViaSdk改用 helper + try/catch/finally 里 cleanup - [P2] Provider Doctor
runLiveProbe()之前也没接入:诊断仍走真实 HOME,可能"诊断绿、聊天红"或反过来的分裂 → 同样改用 helper - 架构原则:凡是会调 Claude SDK 的入口都共享同一套
prepareSdkSubprocessEnv助手,避免逻辑分叉。当前 3 个调用点:streamClaudeSdk/generateTextViaSdk/runLiveProbe - 新回归测试:
claude-home-shadow.test.ts新增 4 个(mirror~/.claude.json的 mcpServers / 同时剥.claude.json的 auth env /.claude.json不存在时不伪造 / settingsJsonHasAuthOverride 也检.claude.json);sdk-subprocess-env.test.ts新增 3 个(env 模式 passthrough / DB provider 走 shadow / cleanup 幂等)
- [P1] shadow HOME 漏镜像