makecindy / makecindy/cindy

手机端经 device-link 发起的新会话丢失 providerId,凭证降级为 fallback 导致 Opus 必现 api_error

Open
#1,883 2 comments 0 reactions 0 assignees View on GitHub
bug
Dominant language
TypeScript
Stars
2.7k
Forks
395
Avg merge
21h 48m
Merged PRs (30d)
776

Description

**提交人**: Yifei Zhou (Leo)
**客户端版本**: 0.1.33

---

## 现象

通过手机端(device-link 远程控制)在被控 Mac 上发起新会话时,`startSession` 的 `providerId` 为 `null`,凭证模式降级为 `fallback`,不再使用 Cindy 内已登录的 Anthropic 订阅凭证,而是回落到 Claude Code CLI 自带的登录态。该内置登录态是 Claude **Pro** 计划,Pro 不提供 Opus,于是首轮对话直接 `api_error`,assistant 零输出。

同一台机器、同一个 App 进程(同一 `endpoint`)、同一模型 `claude-opus-5[1m]`,桌面端发起的会话一切正常。间隔 15 秒创建的两个会话,唯一差异就是凭证字段:

| 创建时间 | session | model | providerId | credentialMode | 结果 |
|:--|:--|:--|:--|:--|:--|
| 14:30:38 | `3c4e9096` | claude-opus-5[1m] | **null** | **fallback** | 报错,零回复 |
| 14:30:54 | `d6587434`(桌面端发起) | claude-opus-5[1m] | anthropic | oauth-bearer | 正常 |
| 14:34:12 | `ce37667e` | claude-opus-5[1m] | **null** | **fallback** | 报错,零回复 |

两次失败的时间点都紧跟在一次手机端接入之后(相隔 13~26 秒):

```
14:30:12 [INFO] [device-link-dispatch] control link opened by
14:30:38 → session 3c4e9096 起,providerId=null / fallback
14:33:59 [INFO] [device-link-dispatch] control link opened by
14:34:12 → session ce37667e 起,providerId=null / fallback
```

扫全部本地会话日志,**57 个历史会话中只有这 2 个是 `credentialMode: fallback`,全部为手机端发起,全部失败**;其余桌面端发起的会话 `providerId` 均被正确解析为 `anthropic` / `openai` / `vectorengine`。

## 复现步骤

1. 桌面端 Cindy 登录 Anthropic provider(Cindy 侧订阅凭证),确认桌面端用 `claude-opus-5` 发起会话正常。
2. 前提:机器上 Claude Code CLI 自带的登录账号是 Claude Pro(即不含 Opus 权限的账号)——这一步是暴露该 bug 的关键,若 CLI 内置账号恰好也有 Opus 权限,降级会被静默掩盖。
3. 开启远程控制,手机端接入 device-link。
4. 在手机端**新建**一个会话,模型选 Claude Code + Opus 5,发任意一句话(如 `hi`)。
5. 会话无任何回复。查看 `/logs/sessions//.ndjson`,`startSession` 中 `providerId: null` / `credentialMode: fallback`。

对照:第 4 步改在桌面端新建会话、再用手机接入该会话继续对话,则完全正常(`providerId: anthropic` / `oauth-bearer`)。这也是目前可用的绕过方式。

## 期望行为

手机端发起会话时,`providerId` 与对应的 provider 凭证应与桌面端发起路径保持一致地传递到 host,Opus 正常可用。

## 实际行为

`providerId` 在远程发起链路上丢失为 `null`,host 侧静默降级为 `fallback` 凭证并继续发起请求,最终失败于模型侧。

SDK 返回的错误原文如下,它指向 CLI 登录态而非真正的原因(远程链路未传 provider),因此从这条提示出发排查会被带偏,只有对比 `startSession` 日志字段才能定位:

```
terminalReason : api_error
stopReason : stop_sequence
output : Claude Opus is not available with the Claude Pro plan.
If you have updated your subscription plan recently,
run /logout and /login for the plan to take effect.
```

## 日志位置

- 会话级:`/logs/sessions//.ndjson`(`startSession` 字段对照 + `SDK ◀ turn ended with error`)
- 主日志:`/logs/main-.log`(`device-link-dispatch` 接入时间线)
- 相关:Claude Code CLI 版本 `2.1.219`

日志中的机器名、设备名、owner id、绝对路径已做脱敏替换,需要原始片段可另行提供。
---
**OS**: darwin arm64 (25.5.0)
**界面语言**: zh-CN

Contributor guide

Open the contributing guide

Research direction

Start with the session-level ndjson logs and the main log’s device-link-dispatch timeline, comparing a mobile-created session with a desktop-created one. Trace the device-link startSession path against the desktop path and follow providerId and credentialMode into the host. Done means mobile-created sessions preserve the selected provider and credentials instead of silently falling back.

Written by the indexing model from the issue text.

Assessment

Tech stack
typescript
Domain
api, authentication
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Active
Clarity
Mostly clear
Newbie friendliness
55/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.