fix(setup): stop panicking on /setup/recover when client is cached #69
No reviewers
Labels
No labels
blocked
bug
documentation
duplicate
enhancement
good first issue
help wanted
invalid
question
waiting-on-julian
wontfix
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
jlxq0/matrix-mcp!69
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "fix/setup-recover-double-init"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Summary
/setup/recoverwas crashing the pod withAlreadyInitializedError(Matrix)when claude.ai had already built the matrix-sdk client for the user (which the/setupprecondition demands). The recover handler calledMatrixClientCache::for_user(&identity, &session.access_token), wheresession.access_tokenis/setup's own unbound OAuth grant — a different token than the one claude.ai installed on the client. The fast path saw mismatched hashes and triedrestore_sessionto swap. matrix-sdk 0.17 rejects double-initialisation → panic → pod crash.Observed live on v0.3.3 during device-binding recovery testing on 2026-05-18: after
MATRIXMCP2was signed out and the claude.ai connector re-OAuthed, alist_joined_roomsbuilt the client at 11:38:20 and a/setup/recoverPOST at 11:38:48 tripped the panic.Fix
MatrixClientCache::get_if_cached(&mxid) -> Option<Arc<Client>>that returns the cached client untouched (norestore_session, no token swap)./setup/recovernow routes throughget_if_cachedinstead offor_user. The recovery runs against Synapse using claude.ai's already-installed device-bound bearer — which is what we actually want, since the unbound/setuptoken has no device scope.for_usersilently drops/setup's token. It didn't.SetupSession::access_tokenandSetupSession::mas_subjectmarked#[allow(dead_code)]with comments — still populated at callback time, no longer routed into the cache. Kept on the session shape for rolling deploys.Test plan
cargo fmt --checkcleancargo clippy --all-targets -- -D warningscleancargo test --all-targets125/125 greenCompat
Server-only change; no MCP surface affected. No client reconnect required.
`/setup/recover`'s precondition demands the cached matrix-sdk client exists for the user (built by claude.ai's connector via a prior tool call). The recover handler then called `MatrixClientCache::for_user(&identity, &session.access_token)`, where `session.access_token` is `/setup`'s own (unbound) OAuth grant — a different access token than the one claude.ai installed on the client. The fast path saw mismatched token hashes and tried to swap via `restore_session`. matrix-sdk 0.17 rejects double-initialisation with `AlreadyInitializedError`, panicking the tokio worker and crashing the pod. Observed live on 2026-05-18 v0.3.3 during device-binding recovery testing: after MATRIXMCP2 was signed out and the claude.ai connector re-OAuthed, a `list_joined_rooms` call built the client at 11:38:20 and a `/setup/recover` POST at 11:38:48 tripped the panic. The misleading comment ("`/setup`'s own unbound access_token is silently dropped by the fast path") was a lie — it wasn't. Fix: add `MatrixClientCache::get_if_cached(&mxid) -> Option<Arc<Client>>` that returns the cached client without touching its session, and route `/setup/recover` through it. The recovery operation runs against Synapse using claude.ai's already-installed device-bound bearer (what we actually want; the unbound `/setup` token has no device scope). Also mark `SetupSession::access_token` and `SetupSession::mas_subject` as `#[allow(dead_code)]` with a comment — they're still populated at callback time but no longer routed into the cache. Kept on the session shape for rolling deploys and future flows. Tests: 125/125 still green. cargo fmt + clippy strict both clean.