#7812·QwenPaw

[Bug]: Slash commands right after desktop startup act on the fallback session (/compact reports an empty memory)

Author: AlexDesign420Created Sep 16, 2026Updated Sep 17, 2026

QwenPaw Version

2.2.1 (desktop/Tauri build from the official release, backend rebuilt from tag v2.2.1 for verification)

Description

Right after the desktop app starts, a slash command typed into the chat box acts on a different conversation than the one on screen. The most visible case is /compact, which answers " No messages to compact. Current memory is empty." while the open chat clearly has messages. The same request in a browser tab against the same backend works.

Root cause chain (verified against a running instance):

  1. The console submits the request with an empty session_id while the route / chat list is still resolving. isSubmissionTargetReady() in console/src/pages/Chat/submissionBizParams.ts returns true whenever the route has no chat id (return !routeChatId || routeChatId === "new" || !!resolvedRouteSessionId;), so nothing holds the submission back, while getSessionIdentity() in console/src/pages/Chat/sessionApi/index.ts still yields sessionId: "".
  2. _extract_session_and_payload() (src/qwenpaw/app/routers/console.py) passes the empty string on, and ConsoleChannel.resolve_session_id() falls back to "<channel>:<sender_id>", i.e. console:default.
  3. The command then executes against that fallback session — an unrelated, stale chat.

Related PR(s): none yet (see Additional Notes)

Security considerations: none — no new inputs, no credential or config handling.

Component(s) Affected

  • Core / Backend (app, agents, config, providers, utils, local_models)
  • Console (frontend web UI)
  • Channels (DingTalk, Feishu, QQ, Discord, iMessage, etc.)
  • Skills
  • CLI
  • Documentation (website)
  • Tests
  • CI/CD
  • Scripts / Deploy

Environment

  • QwenPaw version: 2.2.1
  • OS: macOS 15 (Apple silicon)
  • Install method: official desktop release (Tauri bundle, PyInstaller backend)
  • Python version (if applicable): 3.11 (bundled runtime)

Steps to Reproduce

  1. Have at least one existing conversation with messages.
  2. Start the desktop app (or restart its backend) with that conversation open.
  3. Within the first ~10 seconds after the window appears — before the conversation list has settled — send /compact.

Actual vs Expected

  • Actual: the reply is " No messages to compact. Current memory is empty." The backend loaded the fallback session instead of the open chat, and a ghost chat whose session_id is literally console:default appears in chats.json and gets its updated_at bumped on every such request.
  • Expected: the command acts on the conversation the user has open — or the request is rejected with a clear error. It should never be silently retargeted to another session.

Logs / Screenshots

Failing request from the desktop app (note the session file):

2026-09-16 13:03:40 | INFO | qwenpaw/app/chats/session.py:334 | Load session state from
  .../workspaces/default/sessions/console/default_console--default.json successfully.
2026-09-16 13:03:40 | INFO | qwenpaw/agents/command_handler.py:1336 | Processing command: compact
2026-09-16 13:03:40 | INFO | qwenpaw/app/chats/session.py:303 | Saved session state to
  .../sessions/console/default_console--default.json successfully.

Working request from a browser tab against the same backend — captured body:

json
{"session_id":"1789553264935-bo27ft9","user_id":"default","channel":"console",
 "input":[{"role":"user","content":[{"type":"text","text":"/compact"}]}],"stream":true}
2026-09-16 13:08:34 | INFO | qwenpaw/app/chats/session.py:334 | Load session state from
  .../sessions/console/default_1789553264935-bo27ft9.json successfully.
2026-09-16 13:08:34 | INFO | qwenpaw/agents/command_handler.py:1336 | Processing command: compact
2026-09-16 13:08:47 | INFO | qwenpaw/agents/context/scroll/manager.py:466 | scroll: compact
  timing outcome=done total=13110.5ms ...

Additional Notes

Two independent guards, either of which stops the silent retargeting:

  • Console: do not submit while the target identity is unresolved — drop the !routeChatId shortcut in isSubmissionTargetReady() so an unresolved session blocks the send instead of producing an empty session_id.
  • Backend: treat an explicitly empty session_id as a client error (HTTP 400) in _extract_session_and_payload() instead of letting it reach the channel fallback; a missing field can keep its documented default.

I have the backend guard implemented with unit tests locally ("" and " " → HTTP 400 with a readable message, valid session unchanged) and can open a PR for it if you want it separately from the console-side fix.