Skip to content

Fix identity query hanging indefinitely outside a Tauri runtime - #3189

Open
jefflitt1 wants to merge 2 commits into
block:mainfrom
jefflitt1:upstream-identity-query-fix
Open

jefflitt1 wants to merge 2 commits into
block:mainfrom
jefflitt1:upstream-identity-query-fix

Conversation

@jefflitt1

@jefflitt1 jefflitt1 commented Jul 27, 2026

Copy link
Copy Markdown

Summary

  • The desktop chat UI can be served as a plain web page (no window.__TAURI_INTERNALS__), where get_identity() can never succeed. useIdentityQuery's queryFn always rejects in that environment, and with the default retry config the single configured retry can hit query-core's pause() path if the window isn't focused at that exact moment — canContinue() requires focusManager.isFocused() regardless of networkMode. The query then gets stuck on fetchStatus: "paused" forever instead of ever settling to "error", so anything gating UI on the query's settled status hangs indefinitely.
  • retry: false on useIdentityQuery — a missing native identity backend can't be fixed by retrying, so there's no reason to ever enter that retry/pause branch.
  • Guard get_identity/get_nsec/import_identity/persist_current_identity/sign_out with isTauri(), matching the guard convention already used elsewhere in this codebase (e.g. audioWorklet.ts), so a missing runtime produces a clean, identifiable Error instead of a raw TypeError surfaced from deep inside invoke().

Test plan

  • pnpm build (tsc + vite build) passes
  • pnpm test — 3662/3662 passing
  • pnpm exec biome check on both touched files — clean
  • Manually verified in a browser: served the built bundle as a plain web page outside Tauri and confirmed the identity query now settles to "error" immediately instead of hanging on pending/paused

Related

Checked open PRs/issues for duplicates before submitting — no duplicate found. #3027 is open in an adjacent but different subsystem (relay-side SPA routing config, vs. this PR's desktop-client Tauri guards) for a similarly-motivated self-hosted deployment; not overlapping, noted for context.

@jefflitt1
jefflitt1 requested a review from a team as a code owner July 27, 2026 19:31
@jefflitt1
jefflitt1 force-pushed the upstream-identity-query-fix branch from e056847 to ad7ffe1 Compare August 29, 2026 02:35
@github-actions

github-actions Bot commented Aug 29, 2026

Copy link
Copy Markdown

🔐 Codex Security Review

Status: review required for the current range.

The current range is 00e61eafa917d296104006576b7a2ddbfd58bb5a...0bee0c6dbe0296b6311349a58fa76770e166b2b2.
A new review must complete for this exact range. When manual authorization
is required, a Block organization member must comment exactly
@buzz-security-review 0bee0c6dbe0296b6311349a58fa76770e166b2b2 to authorize a new review.
Any previous review applies only to its recorded range.

When the desktop chat UI is served as a plain web page (no
window.__TAURI_INTERNALS__), get_identity() has no way to succeed, so
useIdentityQuery's queryFn always rejects. With the default retry
config, the single configured retry can call the query-core retryer's
pause() path if the window isn't focused at that exact moment
(canContinue() requires focusManager.isFocused() regardless of
networkMode) — leaving the query stuck on fetchStatus "paused"
forever instead of ever settling to "error". Any consumer gating UI
on this query's settled status (success/error) then hangs
indefinitely, since neither state is ever reached.

- useIdentityQuery: retry: false — a missing native identity backend
  can't be fixed by retrying, so there's no reason to ever enter the
  retry/pause branch.
- tauriIdentity.ts: guard get_identity/get_nsec/import_identity/
  persist_current_identity/sign_out with isTauri(), matching the
  guard convention already used elsewhere in this codebase (e.g.
  audioWorklet.ts), so a missing runtime produces a clean, identifiable
  Error instead of a raw "Cannot read properties of undefined
  (reading 'invoke')" TypeError surfaced from deep inside invoke().

Signed-off-by: Jeff Litt <jglittell@gmail.com>
@jefflitt1
jefflitt1 force-pushed the upstream-identity-query-fix branch from ad7ffe1 to c34ad89 Compare August 29, 2026 10:12
Covers the actual failure mode this PR fixes: get_identity and its
siblings must never reach invokeTauri when there is no Tauri runtime.
Without this guard the query hung on fetchStatus "paused" instead of
settling to "error" — these tests assert the guard short-circuits
before any native call is attempted, and that it doesn't when Tauri
is available.

The "Tauri available" happy-path case lives in its own file
(tauriIdentity.available.test.mjs) rather than alongside the
"unavailable" cases: node's per-process ESM module cache means mixing
an isTauri: () => true mock with an isTauri: () => false mock in the
same test file causes a later import of tauriIdentity.ts to return
the first test's cached module instance instead of re-evaluating
against the new mock. node --test already runs each matched file in
its own process, which sidesteps that.

Requires --experimental-test-module-mocks (added to the test script)
for node:test's t.mock.module — available in the project's pinned
Node version (24.15.0) but not previously enabled.

Signed-off-by: Jeff Litt <jglittell@gmail.com>
@jefflitt1
jefflitt1 force-pushed the upstream-identity-query-fix branch from c34ad89 to 0bee0c6 Compare August 29, 2026 15:34
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant