Skip to content

桌面 App 与浏览器助手宿主并存时,对桌面端已打开的会话发消息报 SessionAlreadyOwnedError #112

Open

Description

@jeavenguitar

桌面 App 与浏览器助手宿主并存时,对桌面端已打开的会话发消息报 SessionAlreadyOwnedError

环境

项目 值
系统 macOS(Apple Silicon)
dsh-browser 0.1.4(commit 23895d7,main 最新)
DSH runtime @deepseek-ai/dsh@0.2.0-rc.2(dsh-browser 工作区固定版本)
Chrome 扩展 dsh 浏览器助手 0.1.4
DSH 桌面 App /Applications/DeepSeek Harness.app
DSH_HOME /Users/<user>/.dsh(桌面 App 与浏览器助手宿主共用同一个)

复现步骤

  1. 打开 DSH 桌面 App,打开任意一个工作区会话(例如 ~/project-a 下的 session-XXXX),保持该会话处于打开状态。
    1. 确认浏览器桥宿主已被拉起(browser-follow-desktop.py 会在检测到桌面 App 运行时自动启动它):
  2. $ lsof -nP -iTCP:3080 -sTCP:LISTEN
  3. node 24838 ... TCP 127.0.0.1:3080 (LISTEN)
    1. 在 Chrome 里打开 dsh 浏览器助手侧边栏,切换/选中第 1 步那个已在桌面端打开的会话。
    1. 在侧边栏输入任意消息并发送。

实际结果

发送失败,报错:

session "session-2c566034-8fdd-4b06-9b7a-9adaacc4073b" is already owned by an active write handle

期望结果

应该能正常发送;或至少给出可操作的提示(例如"该会话正被桌面 App 占用,是否改为新建会话/只读跟随"),而不是抛出一个底层持久化错误。

补充观察(有助于定位)

  • 在侧边栏新建会话可以正常发消息;只有复用桌面端已打开的会话才会失败。
    • 浏览器助手自己创建的会话(落在 ~/.dsh/sessions/--Users-<user>-.dsh-browser-sessions--/)全部无锁,工作正常。
      • 该问题与 dsh-browser 版本无关:升级到 main 最新(23895d7)后依然复现。

证据

1. 报错来自 DSH 的 session 持久化层

错误类型定义在 @deepseek-ai/dsh-session-persistence:

// lib/index.js
var SessionAlreadyOwnedError = class extends Error {
  constructor(sessionId) {
    super(`session "${sessionId}" is already owned by an active write handle`);
    this.name = "SessionAlreadyOwnedError";
  }
};

实际抛出方是 0.2.0-rc.2 新增的 JSONL 后端 @deepseek-ai/dsh-session-persistence-jsonl。它的设计说明(lib/types/lease.d.ts)写得很明确:

Cross-process write-ownership lock for one session's artifact directory, held for the whole life of a write handle. The arbiter is the kernel: POSIX takes a non-blocking flock(2) via native system support on session.lock beside the log. Contention maps to SessionAlreadyOwnedError.

A live but wedged holder keeps the lock until its process exits: there is deliberately no expiry that could expropriate a stalled writer.

也就是说:写所有权是内核级 flock(2),非阻塞、跨进程互斥、无超时抢占。

2. 锁确实被桌面 App 持有(lsof 实测)

$ lsof ~/.dsh/sessions/--Applications-EServer-www-gfh-api--/session-2c566034-.../session.lock
COMMAND     PID   USER   FD   TYPE  NAME
DeepSeek  24428   user   55w   REG   .../session-2c566034-.../session.lock

PID 24428 就是 DSH 桌面 App 的主进程:

$ ps -p 24428 -o command=
/Applications/DeepSeek Harness.app/Contents/MacOS/DeepSeek Harness --expose-internals .../dsh-desktop-host/lib/index.js ... ~/.dsh/profiles/desktop ...

3. 两个宿主并存,且共用同一个 DSH_HOME

# 桌面 App 宿主(19387)
PID 24428  /Applications/DeepSeek Harness.app/Contents/MacOS/DeepSeek Harness ...

# 浏览器桥宿主(3080)—— 由 ~/.dsh/scripts/browser-follow-desktop.py 自动拉起
PID 24838  node ~/.dsh/dsh-browser/node_modules/@deepseek-ai/dsh/lib/bin.js \
                web --no-open --host 127.0.0.1 --port 3080

两个进程的 DSH_HOME 都是 /Users/<user>/.dsh,因此共用同一份 sessions/ 目录,必然争抢同一个 session.lock。

4. 冲突链路

① 桌面 App 打开会话 X                  → PID 24428 取得 X/session.lock(内核 flock 独占)
              ↓
② 浏览器侧边栏对会话 X 发消息
              ↓
③ 扩展 → 3080 宿主(PID 24838)请求对 X 开写句柄
              ↓
④ 内核 flock 已被 24428 独占,且官方"故意不做超时抢占"
              ↓
⑤ SessionAlreadyOwnedError ✗

根因判断

dsh-browser 的架构前提是「再起一个全功能 DSH 宿主,并与桌面 App 共享同一个 DSH_HOME」——scripts/browser-follow-desktop.py 的存在正说明这是有意设计。

在 DSH 0.1.x 时代,写所有权只在进程内记账,两个宿主各写各的,虽然不严谨但"能用"。DSH 0.2.0-rc.2 换成了内核级跨进程 flock 后,这个前提就破产了:任何被桌面 App 打开的会话,浏览器侧边栏都无法再取得写句柄。

这不是 DSH 的 bug(跨进程锁是必要的加固),而是 dsh-browser 在这个新约束下缺少冲突处理。

Activity

  1. Lum1104 commented on Oct 7, 2026

    @Lum1104
    Member

    感谢反馈!

  2. jeavenguitar commented on Oct 7, 2026

    @jeavenguitar
    Author

    不客气,希望能修复这个问题

  3. graydovee commented on Oct 17, 2026

    @graydovee

    这不是 dsh-browser 的缺陷,而是「两个 dsh 宿主共用同一个 DSH_HOME」时的使用问题。

    dsh 0.2.x 起,会话的写所有权是内核级、按会话、跨进程独占,且有意不做超时抢占(POSIX flock / Windows 具名信号量)。桌面 App 的宿主打开会话 X 时持有 X 的写句柄;此时再起一个共用 ~/.dsh 的 dsh web,第二个宿主对 X 只能读不能写,于是报 SessionAlreadyOwnedError。两个 dsh web 之间也是同样情况。新建会话不受影响,是因为它由第二个宿主自己创建、自己持有。

    正确做法:不要为了浏览器面板再起一个宿主,让侧栏直接连桌面 App 的宿主即可。

    之前你额外起一个3080端口的web,可能因为桌面端的本地端口每次启动都随机、面板探不到。这一点已经在 PR #121 的最新提交里解决:现在面板打开时能扫描端口自动发现桌面宿主。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions