tinyhumansai/openhuman · error · Error

Not running in Tauri

Error message

Not running in Tauri

What it means

exchangeToken() wraps the Tauri IPC command `exchange_token`, which exchanges a login token for a session token inside the Rust shell. The isTauri() guard at the top fails fast when the invoke bridge does not exist — plain browser, Vitest node environment, SSR — instead of calling an undefined bridge and surfacing an opaque TypeError.

Source

Thrown at app/src/utils/tauriCommands/auth.ts:20

 * Authentication commands.
 */
import { callCoreRpc } from '../../services/coreRpcClient';
// `safeInvoke` (aliased to `invoke`) replaces bare
// `@tauri-apps/api/core::invoke` so the CEF `window.ipc.postMessage`
// synchronous throw (Sentry TAURI-REACT-7 / TAURI-REACT-6) surfaces as a
// rejected Promise. `exchangeToken` runs early in the auth flow where the
// CEF bridge can still be unwired, so this matters most.
import { type CommandResponse, safeInvoke as invoke, isTauri } from './common';

/**
 * Exchange a login token for a session token
 */
export async function exchangeToken(
  backendUrl: string,
  token: string
): Promise<{ sessionToken: string; user: object }> {
  if (!isTauri()) {
    throw new Error('Not running in Tauri');
  }

  return await invoke('exchange_token', { backendUrl, token });
}

/**
 * Get the current authentication state from Rust
 */
export async function getAuthState(): Promise<{ is_authenticated: boolean; user: object | null }> {
  if (!isTauri()) {
    return { is_authenticated: false, user: null };
  }

  const response = await callCoreRpc<{ result: { isAuthenticated: boolean; user: object | null } }>(
    { method: 'openhuman.auth_get_state' }
  );

  return { is_authenticated: response.result.isAuthenticated, user: response.result.user };

View on GitHub (pinned to a221052e0d)

Solutions

  1. Run the login flow inside the desktop shell: `pnpm dev:app` or the packaged app.
  2. In Vitest, mock the module: vi.mock('@/utils/tauriCommands/auth', ...) and return a fake session.
  3. Gate the call site on isTauri() and show a desktop-only notice in browser contexts.
  4. For a web target, exchange the token against the backend HTTP endpoint directly instead of Tauri IPC.

Example fix

// before
const { sessionToken } = await exchangeToken(backendUrl, token);
// after
import { isTauri } from '@/utils/tauriCommands/common';
if (!isTauri()) throw new Error('Sign-in is available in the desktop app only.');
const { sessionToken } = await exchangeToken(backendUrl, token);
Defensive patterns

Strategy: validation

Validate before calling

import { isTauri } from '@/utils/tauriCommands/common';

if (!isTauri()) {
  // browser/test context: hide sign-in or use an HTTP-only auth path
}

Try / catch

try {
  const session = await exchangeToken(backendUrl, token);
} catch (err) {
  if (err instanceof Error && err.message === 'Not running in Tauri') {
    showDesktopOnlyNotice();
  } else {
    throw err;
  }
}

Prevention

When it happens

Trigger: Calling exchangeToken(backendUrl, token) from UI running under `pnpm dev` (Vite-only, no Tauri host), from a unit test that imports the real module instead of a mock, or from any non-desktop embedding of the frontend.

Common situations: Styling the login flow in Chrome via the Vite dev server; Vitest specs that forget to vi.mock the auth commands; opening the built assets on a plain web server and walking the auth path.

Related errors


AI-assisted analysis of tinyhumansai/openhuman@a221052e0d (2026-08-16). Data as JSON: /api/errors/c58affe04671eafc. Report an issue: GitHub.