{"record":{"id":"b26f94561a5ff4df","repo":"jlcodes99/cockpit-tools","slug":"claude-login-start","errorCode":null,"errorMessage":"Claude login start 响应缺少关键字段","messagePattern":"Claude login start 响应缺少关键字段","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/services/claudeService.ts","lineNumber":41,"sourceCode":"};\n\nexport interface ClaudeCliLaunchInfo {\n  accountId: string;\n  accountEmail: string;\n  workingDir: string;\n  launchCommand: string;\n}\n\nfunction normalizeClaudeDesktopLoginStartResponse(\n  raw: ClaudeDesktopLoginStartResponseRaw,\n): ClaudeDesktopLoginStartResponse {\n  const loginId = raw.loginId ?? raw.login_id ?? '';\n  const userDataDir = raw.userDataDir ?? raw.user_data_dir ?? '';\n  const expiresIn = Number(raw.expiresIn ?? raw.expires_in ?? 0);\n  const intervalSeconds = Number(raw.intervalSeconds ?? raw.interval_seconds ?? 0);\n\n  if (!loginId || !userDataDir) {\n    throw new Error('Claude login start 响应缺少关键字段');\n  }\n\n  return {\n    loginId,\n    userDataDir,\n    expiresIn: Number.isFinite(expiresIn) && expiresIn > 0 ? expiresIn : 1800,\n    intervalSeconds: Number.isFinite(intervalSeconds) && intervalSeconds > 0 ? intervalSeconds : 2,\n  };\n}\n\nfunction normalizeClaudeOAuthStartResponse(raw: ClaudeOAuthStartResponseRaw): ClaudeOAuthStartResponse {\n  const loginId = raw.loginId ?? raw.login_id ?? '';\n  const verificationUri = raw.verificationUri ?? raw.verification_uri ?? '';\n  const expiresIn = Number(raw.expiresIn ?? raw.expires_in ?? 0);\n  const intervalSeconds = Number(raw.intervalSeconds ?? raw.interval_seconds ?? 0);\n\n  if (!loginId || !verificationUri) {\n    throw new Error('Claude OAuth start 响应缺少关键字段');","sourceCodeStart":23,"sourceCodeEnd":59,"githubUrl":"https://github.com/jlcodes99/cockpit-tools/blob/1ed8b77992d62ca81fabf744deb0839ad361d5bf/src/services/claudeService.ts#L23-L59","documentation":"normalizeClaudeDesktopLoginStartResponse validates the backend response for starting a Claude Desktop login and throws when neither loginId nor userDataDir can be extracted. Those two fields are mandatory to track the pending login session; numeric fields get safe defaults instead.","triggerScenarios":"The Tauri backend response for Claude Desktop login start omits or empties loginId/login_id and/or userDataDir/user_data_dir (both camelCase and snake_case are checked).","commonSituations":"Backend command updated or failing silently, returning an error object instead of the login payload, a mismatched contract between frontend types and the Rust command, or the browser session failing to spawn so no user data dir is produced.","solutions":["Log the raw response from the backend command to see which fields are actually returned","Verify the Rust command returns loginId and userDataDir (or login_id/user_data_dir) and that the Tauri serde rename attributes match","Update frontend normalization to match any recent backend field rename","Retry the login after confirming the Claude Desktop binary/environment is available"],"exampleFix":"// before\nconst raw = await invoke('claude_desktop_login_start'); // returned {}\n// after\n// backend: return Ok(json!({ \"loginId\": id, \"userDataDir\": dir })) and assert fields in tests","handlingStrategy":"try-catch","validationCode":"const raw = /* backend response */;\nconst loginId = raw?.loginId ?? raw?.login_id ?? '';\nconst userDataDir = raw?.userDataDir ?? raw?.user_data_dir ?? '';\nif (!loginId || !userDataDir) throw new Error('Claude login start 响应缺少关键字段');","typeGuard":"function isValidLoginStart(r: unknown): r is { loginId: string; userDataDir: string } {\n  const o = r as any;\n  return !!(o && (o.loginId || o.login_id) && (o.userDataDir || o.user_data_dir));\n}","tryCatchPattern":"try {\n  const start = await claudeDesktopLoginStart();\n} catch (e) {\n  if (e instanceof Error && e.message.includes('缺少关键字段')) {\n    showLoginError('Login start failed: incomplete response from backend.');\n  } else throw e;\n}","preventionTips":["Keep backend DTO field names (or serde renames) in sync with the frontend normalizer","Log raw backend payloads during development","Add backend tests asserting loginId and userDataDir are always present on success","Map provider/backend errors to Err instead of returning partial success objects"],"tags":["validation","oauth","tauri","claude"],"backgroundTag":"missing-response-field","analyzedSha":"1ed8b77992d62ca81fabf744deb0839ad361d5bf","analyzedAt":"2026-09-05T09:51:41.178Z","contentChangedAt":"2026-09-05T09:51:41.178Z","schemaVersion":2},"datasetVersion":"2026-09-12T12:17:11.808Z"}