jlcodes99/cockpit-tools · error
Claude OAuth start 响应缺少关键字段
Error message
Claude OAuth start 响应缺少关键字段
What it means
normalizeClaudeOAuthStartResponse validates the Claude OAuth start response and throws when loginId or verificationUri (snake_case variants accepted) is missing or empty. Both are required to continue the device-authorization flow; expiry and interval get defaults.
Source
Thrown at src/services/claudeService.ts:59
throw new Error('Claude login start 响应缺少关键字段');
}
return {
loginId,
userDataDir,
expiresIn: Number.isFinite(expiresIn) && expiresIn > 0 ? expiresIn : 1800,
intervalSeconds: Number.isFinite(intervalSeconds) && intervalSeconds > 0 ? intervalSeconds : 2,
};
}
function normalizeClaudeOAuthStartResponse(raw: ClaudeOAuthStartResponseRaw): ClaudeOAuthStartResponse {
const loginId = raw.loginId ?? raw.login_id ?? '';
const verificationUri = raw.verificationUri ?? raw.verification_uri ?? '';
const expiresIn = Number(raw.expiresIn ?? raw.expires_in ?? 0);
const intervalSeconds = Number(raw.intervalSeconds ?? raw.interval_seconds ?? 0);
if (!loginId || !verificationUri) {
throw new Error('Claude OAuth start 响应缺少关键字段');
}
return {
loginId,
verificationUri,
expiresIn: Number.isFinite(expiresIn) && expiresIn > 0 ? expiresIn : 600,
intervalSeconds: Number.isFinite(intervalSeconds) && intervalSeconds > 0 ? intervalSeconds : 1,
};
}
export async function listClaudeAccounts(): Promise<ClaudeAccount[]> {
return await invoke('list_claude_accounts');
}
export async function deleteClaudeAccount(accountId: string): Promise<void> {
return await invoke('delete_claude_account', { accountId });
}
View on GitHub (pinned to 1ed8b77992)
Solutions
- Log the raw invoke response to identify the missing field
- Confirm the Rust OAuth start command maps the provider response's verification_uri into verificationUri and returns a loginId
- Check Claude OAuth endpoint availability and credentials (client ID) if the provider returned an error instead
- Align frontend normalization with any renamed backend fields
Example fix
// before
// backend forwarded provider error body { "error": "invalid_client" }
// after
// backend: map errors to Err and only resolve Ok with { loginId, verificationUri, ... } Defensive patterns
Strategy: try-catch
Validate before calling
const raw = /* backend OAuth response */;
const loginId = raw?.loginId ?? raw?.login_id ?? '';
const verificationUri = raw?.verificationUri ?? raw?.verification_uri ?? '';
if (!loginId || !verificationUri) throw new Error('Claude OAuth start 响应缺少关键字段'); Type guard
function isValidOauthStart(r: unknown): r is { loginId: string; verificationUri: string } {
const o = r as any;
return !!(o && (o.loginId || o.login_id) && (o.verificationUri || o.verification_uri));
} Try / catch
try {
const start = await claudeOauthLoginStart();
} catch (e) {
if (e instanceof Error && e.message.includes('缺少关键字段')) {
showOauthError('OAuth start failed: incomplete response — check backend/provider.');
} else throw e;
} Prevention
- Assert verificationUri is mapped from the provider's verification_uri in backend tests
- Check Claude OAuth endpoint health and client credentials before starting the flow
- Forward provider error bodies as errors, never as partial payloads
- Version the backend/frontend DTO contract and test both camelCase and snake_case variants
When it happens
Trigger: The backend OAuth start/prepare command returns a payload without a usable loginId or verificationUri/verification_uri field.
Common situations: OAuth provider returning an error body that the backend forwards verbatim, backend/DTO field-name mismatch after a refactor, network or auth-server outage producing an incomplete response.
Related errors
AI-assisted analysis of jlcodes99/cockpit-tools@1ed8b77992 (2026-09-05).
Data as JSON: /api/errors/d1dfacb7f80eb330.
Report an issue: GitHub.