lbjlaq/Antigravity-Manager · error · Error

common.tauri_api_not_loaded

Error message

common.tauri_api_not_loaded

What it means

Thrown by ensureTauriEnvironment() (src/services/accountService.ts:9) when the imported `request` from src/utils/request.ts is not a function. The comment in the source admits this should never fire in normal builds, because request.ts statically exports `async function request<T>()` for both Tauri and Web modes. In practice it means the module evaluated to undefined at call time: a circular import (request.ts pulling in a module that imports accountService), a test mock returning `{}` instead of a function, or broken tree-shaking/bundling. Only the Tauri-only OAuth functions in this service call the guard (e.g. startOAuthLogin), so the blast radius is OAuth flows.

Source

Thrown at src/services/accountService.ts:9

import i18n from '../i18n';
import { Account, DeviceProfile, DeviceProfileVersion, QuotaData } from '../types/account';
import { request as invoke } from '../utils/request';

// 检查环境 (可选)
function ensureTauriEnvironment() {
    // Web 模式下 request 也是一个 function,所以这里不应抛错
    if (typeof invoke !== 'function') {
        throw new Error(i18n.t('common.tauri_api_not_loaded'));
    }
}

export async function listAccounts(): Promise<Account[]> {
    const response = await invoke<any>('list_accounts');
    // 如果返回的是对象格式 { accounts: [...] }, 则取其 accounts 属性
    if (response && typeof response === 'object' && Array.isArray(response.accounts)) {
        return response.accounts;
    }
    // 否则直接返回响应内容(假设为数组)
    return response || [];
}

export async function getCurrentAccount(): Promise<Account | null> {
    return await invoke('get_current_account');
}

export async function addAccount(email: string, refreshToken: string): Promise<Account> {

View on GitHub (pinned to a2e3c45423)

Solutions

  1. Run `npx madge --circular src` (or similar) and break any cycle between src/utils/request.ts and src/services/accountService.ts by moving shared imports into a third module.
  2. If this fires in tests, fix the mock: jest.mock('../utils/request', () => ({ request: jest.fn() })) so the named export is a function.
  3. Replace the dead `typeof invoke !== 'function'` check with a real environment check on window.__TAURI_INTERNALS__ / window.__TAURI__ (the flag request.ts:2 already uses), since in Web mode request is intentionally a function and the current guard checks the wrong thing.
  4. Verify the bundler config (vite/webpack aliases, optimizeDeps) is not duplicating or transforming src/utils/request.ts so the named export survives.

Example fix

// before (src/services/accountService.ts)
function ensureTauriEnvironment() {
    if (typeof invoke !== 'function') {
        throw new Error(i18n.t('common.tauri_api_not_loaded'));
    }
}

// after: guard the real condition — Tauri-only commands need the desktop shell
const isTauriShell = typeof window !== 'undefined' &&
    (!!(window as any).__TAURI_INTERNALS__ || !!(window as any).__TAURI__);
function ensureTauriEnvironment() {
    if (!isTauriShell) {
        throw new Error(i18n.t('common.tauri_api_not_loaded'));
    }
}
Defensive patterns

Strategy: validation

Validate before calling

// Before calling OAuth service functions, verify the adapter actually loaded
import { request as invoke } from '../utils/request';

export function canUseAccountService(): boolean {
  return typeof invoke === 'function';
}

// caller
if (!canUseAccountService()) {
  console.error('request module failed to load (circular import?)');
  return;
}

Type guard

type InvokeFn = (cmd: string, args?: unknown) => Promise<unknown>;
const isInvokeFn = (v: unknown): v is InvokeFn => typeof v === 'function';

Try / catch

try {
  await startOAuthLogin();
} catch (e) {
  if (e instanceof Error && e.message.includes('tauri_api_not_loaded')) {
    // module-load problem, not a user mistake: log loudly, don't retry
    console.error('[accountService] adapter missing — check circular imports / mocks:', e);
    throw e;
  }
  throw e;
}

Prevention

When it happens

Trigger: Calling startOAuthLogin()/other OAuth functions in accountService when the `import { request as invoke }` binding resolved to undefined — typically after introducing a circular import chain through ../utils/request, or when a unit test mocks '../utils/request' with jest.mock without a function export, or after an aggressive bundle transform drops the re-exported binding.

Common situations: Adding an import to request.ts (e.g. a logger or i18n helper) that transitively imports accountService.ts, creating a cycle; Vitest/Jest mocks that forget `mockReturnValue`/export shape; mixing CJS/ESM interop where the named export is attached under .default; HMR leaving a stale module during development.

Related errors


AI-assisted analysis of lbjlaq/Antigravity-Manager@a2e3c45423 (2026-08-16). Data as JSON: /api/errors/edc9f9b7d0d68158. Report an issue: GitHub.