lbjlaq/Antigravity-Manager · error · Error

accounts.add.oauth.error_no_url

Error message

accounts.add.oauth.error_no_url

What it means

handleOAuthWeb() in src/components/accounts/AddAccountDialog.tsx:287 throws this after invoke('prepare_oauth_url') resolves but yields no usable URL — neither a non-empty string nor a res.url property. In web mode that command maps to GET /api/auth/url (request.ts:114); in Tauri it is a native command. The guard exists because window.open(undefined) would silently open about:blank, so the dialog fails fast and keeps the manual-copy UI honest.

Source

Thrown at src/components/accounts/AddAccountDialog.tsx:287

            // 不自动关闭,让用户看到结果
        } else {
            // 全部失败
            setStatus('error');
            setMessage(t('accounts.add.token.batch_fail'));
        }
    };

    const handleOAuthWeb = async () => {
        try {
            setStatus('loading');
            setMessage(t('accounts.add.oauth.btn_start') + '...');

            // 1. 获取 URL (指向 /auth/callback)
            const res = await invoke<any>('prepare_oauth_url');
            const url = typeof res === 'string' ? res : res.url;

            if (!url) {
                throw new Error(t('accounts.add.oauth.error_no_url', 'OAuth URLを取得できませんでした'));
            }

            setOauthUrl(url); // 确保链接在 UI 中可见,方便用户手动复制

            // 2. 打开新标签页 (响应用户反馈:Web 端直接使用新标签体验更好)
            const popup = window.open(url, '_blank');

            if (!popup) {
                setStatus('error');
                setMessage(t('accounts.add.oauth.popup_blocked', 'ポップアップがブロックされました'));
                return;
            }

            // 3. 监听消息
            const handleMessage = async (event: MessageEvent) => {
                // 安全检查: 如果定义了 ORIGIN 校验更好,这里暂时检查 data type
                if (event.data?.type === 'oauth-success') {
                    popup.close();

View on GitHub (pinned to a2e3c45423)

Solutions

  1. Hit GET /api/auth/url (or invoke prepare_oauth_url in the desktop shell) directly and inspect the payload; configure the missing OAuth client credentials if it returns empty.
  2. Tighten the response handling: accept string | { url }, validate it starts with https?://, and log the raw response when invalid so the real shape is visible.
  3. If a 204/empty body is legitimate, have the backend return a structured error (e.g. { error: 'oauth_not_configured' }) and surface that instead of the generic no-url error.
  4. Check list_oauth_clients / get_active_oauth_client output to confirm an OAuth client is actually configured before opening the dialog.

Example fix

// before
const res = await invoke<any>('prepare_oauth_url');
const url = typeof res === 'string' ? res : res.url;
if (!url) {
    throw new Error(t('accounts.add.oauth.error_no_url', 'OAuth URLを取得できませんでした'));
}

// after
const res = await invoke<string | { url?: string } | null>('prepare_oauth_url');
const url = typeof res === 'string' ? res : res?.url;
if (typeof url !== 'string' || !/^https?:\/\//.test(url)) {
    console.error('prepare_oauth_url returned unexpected payload:', res);
    throw new Error(t('accounts.add.oauth.error_no_url', 'OAuth URLを取得できませんでした'));
}
Defensive patterns

Strategy: type-guard

Validate before calling

// Validate the response shape before using it
const res = await invoke<unknown>('prepare_oauth_url');
const candidate = typeof res === 'string' ? res : (res as { url?: unknown })?.url;
if (typeof candidate !== 'string' || !/^https?:\/\//.test(candidate)) {
  throw new Error('OAuth provider not configured — empty authorize URL');
}

Type guard

type OAuthUrlResponse = string | { url: string };
const isUsableOAuthUrl = (r: unknown): r is OAuthUrlResponse =>
  (typeof r === 'string' && /^https?:\/\//.test(r)) ||
  (typeof r === 'object' && r !== null &&
    typeof (r as { url?: unknown }).url === 'string' &&
    /^https?:\/\//.test((r as { url: string }).url));

Try / catch

try {
  const res = await invoke('prepare_oauth_url');
  if (!isUsableOAuthUrl(res)) {
    setOauthUrl(null);
    setStatus('error');
    setMessage(t('accounts.add.oauth.error_no_url'));
    return; // keep dialog open so the user can retry after fixing config
  }
  window.open(extractUrl(res), '_blank');
} catch (e) {
  setStatus('error');
  setMessage(e instanceof Error ? e.message : String(e));
}

Prevention

When it happens

Trigger: Backend responds 200 with an empty string, null, or an object without url (e.g. { ok: false } or an error envelope that request.ts passed through); OAuth client credentials (client_id/secret) not configured server-side so the authorize URL cannot be built; a proxy returning an HTML error page that request.ts:261-266 coerces to a truthy string but the code path returned a parsed object instead.

Common situations: First-time setup where the OAuth provider (Z.ai/Google) client is not configured before the user clicks 'Start OAuth'; web deployment where GET /api/auth/url is misrouted or returns an empty body with 204 (request.ts:252-254 returns null → url undefined); backend version drift changing the response shape from a bare URL string to { url } or vice versa while frontend only tolerates two of the shapes.

Related errors


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