From 5c9ebe1930bb204009b97d81b677c5c0becc6319 Mon Sep 17 00:00:00 2001 From: baiqing Date: Fri, 15 May 2026 09:20:43 +0800 Subject: [PATCH 1/2] fix(asr): surface Volcengine 403 as 'credentials rejected' instead of raw HTTP error MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 用户反馈"豆包云端语音连接不了",日志显示 9 次相同的: [coord] open ASR session failed: connection failed: HTTP error: 403 Forbidden 原因不是网络不通(网络通到 Volcengine 才会有 HTTP 状态码),而是凭据被拒: App ID / Access Token 错、或账号没开通 SAUC bigmodel 资源。当前 capsule 显示原始 `HTTP error: 403 Forbidden`,用户看不懂、客服需要解释一遍。 修法:在 volcengine.rs 加 classify_connect_error 函数,握手收到 401/403 时 转成新的 VolcengineASRError::AuthRejected(status) variant;它的 thiserror display 直接是中文「凭据被拒(HTTP 403):请检查 Settings → 凭据 → Volcengine 的 App ID 和 Access Token,或确认账号已开通 SAUC bigmodel 资源」。 coordinator 沿用原有错误透传路径,capsule 文案自动从英文变中文具体引导。 其它握手错误(DNS / TLS / connection refused)仍走 ConnectionFailed,文案不变。 cargo test 258 全过。 --- .../app/src-tauri/src/asr/volcengine.rs | 24 ++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/openless-all/app/src-tauri/src/asr/volcengine.rs b/openless-all/app/src-tauri/src/asr/volcengine.rs index 7d28fc05..3e2c5b98 100644 --- a/openless-all/app/src-tauri/src/asr/volcengine.rs +++ b/openless-all/app/src-tauri/src/asr/volcengine.rs @@ -50,6 +50,12 @@ pub enum VolcengineASRError { CredentialsMissing, #[error("connection failed: {0}")] ConnectionFailed(String), + /// WebSocket 握手阶段服务端返回 401 / 403:凭据被拒。 + /// 区分自 `ConnectionFailed`(DNS/TLS/网络层失败)—— 前者通常是 App ID / Access + /// Token / Resource ID 错或账号没开通 bigmodel;后者是网络断 / 防火墙 / DNS。 + /// 显式 variant 让 coordinator 在 capsule 给用户中文「检查凭据」提示,不是 raw HTTP error。 + #[error("Volcengine 凭据被拒(HTTP {0}):请检查 Settings → 凭据 → Volcengine 的 App ID 和 Access Token,或确认账号已开通 SAUC bigmodel 资源")] + AuthRejected(u16), #[error("authentication failed")] AuthenticationFailed, #[error("no final result")] @@ -155,9 +161,7 @@ impl VolcengineStreamingASR { .map_err(|e| VolcengineASRError::ConnectionFailed(e.to_string()))?, ); - let (ws, _resp) = connect_async(request) - .await - .map_err(|e| VolcengineASRError::ConnectionFailed(e.to_string()))?; + let (ws, _resp) = connect_async(request).await.map_err(classify_connect_error)?; let (write, read) = ws.split(); let (tx, rx) = oneshot::channel(); @@ -637,6 +641,20 @@ fn normalized_result(json: &Value) -> Option<&Value> { None } +/// 把 tokio-tungstenite 的 connect 错误分类:握手收到 HTTP 401 / 403 → `AuthRejected` +/// (凭据被拒,要 user 检查 App ID / Access Token / 账号资源开通状态);其它 → 通用 +/// `ConnectionFailed`(DNS / TLS / 网络层)。让 capsule 文案能跟泛泛 HTTP error 区分。 +fn classify_connect_error(err: tokio_tungstenite::tungstenite::Error) -> VolcengineASRError { + use tokio_tungstenite::tungstenite::Error as WsError; + if let WsError::Http(resp) = &err { + let status = resp.status().as_u16(); + if status == 401 || status == 403 { + return VolcengineASRError::AuthRejected(status); + } + } + VolcengineASRError::ConnectionFailed(err.to_string()) +} + fn hotword_context(entries: &[DictionaryHotword]) -> Option { let mut seen: Vec = Vec::new(); for entry in entries { From 1234dcd9c025148f96d8f5c005f289ef14db5739 Mon Sep 17 00:00:00 2001 From: baiqing Date: Fri, 15 May 2026 09:28:07 +0800 Subject: [PATCH 2/2] =?UTF-8?q?fix(asr):=20shorten=20403=20message=20to=20?= =?UTF-8?q?'=E5=87=AD=E6=8D=AE=E8=A2=AB=E6=8B=92=EF=BC=88403=EF=BC=89'?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 原文案带 Settings 引导 + SAUC 资源说明,capsule 上显得拥挤。简化到 5 个字 + 状态码,原因留在错误类型 doc comment 里。 --- openless-all/app/src-tauri/src/asr/volcengine.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/openless-all/app/src-tauri/src/asr/volcengine.rs b/openless-all/app/src-tauri/src/asr/volcengine.rs index 3e2c5b98..83708fe2 100644 --- a/openless-all/app/src-tauri/src/asr/volcengine.rs +++ b/openless-all/app/src-tauri/src/asr/volcengine.rs @@ -53,8 +53,8 @@ pub enum VolcengineASRError { /// WebSocket 握手阶段服务端返回 401 / 403:凭据被拒。 /// 区分自 `ConnectionFailed`(DNS/TLS/网络层失败)—— 前者通常是 App ID / Access /// Token / Resource ID 错或账号没开通 bigmodel;后者是网络断 / 防火墙 / DNS。 - /// 显式 variant 让 coordinator 在 capsule 给用户中文「检查凭据」提示,不是 raw HTTP error。 - #[error("Volcengine 凭据被拒(HTTP {0}):请检查 Settings → 凭据 → Volcengine 的 App ID 和 Access Token,或确认账号已开通 SAUC bigmodel 资源")] + /// 文案简短,原因在文档里说明,capsule 不堆长引导。 + #[error("凭据被拒({0})")] AuthRejected(u16), #[error("authentication failed")] AuthenticationFailed,