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
- 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.
- 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.
- 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.
- 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
- Configure and verify the OAuth client (via list_oauth_clients / get_active_oauth_client) before rendering the OAuth button.
- Make the backend return a structured error for 'not configured' instead of an empty 200 so the frontend can show a precise message.
- Validate the URL with a regex (^https?://) — truthiness accepts whitespace or malformed strings.
- Always log the raw prepare_oauth_url payload when validation fails so response-shape drift is diagnosed in one step.
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.