{"record":{"id":"d8b953258adabded","repo":"yikart/AiToEarn","slug":"error-d8b953","errorCode":null,"errorMessage":"邮箱参数不能为空","messagePattern":"邮箱参数不能为空","errorType":"validation","errorClass":"BadRequestException","httpStatus":400,"severity":"warning","filePath":"project/aitoearn-electron/server/src/modules/plat/youtube/youtube.controller.ts","lineNumber":56,"sourceCode":"    private readonly youtubeService: YoutubeService,\n    private readonly youtubeAuthService: YouTubeAuthService,\n    ) {}\n\n  @ApiOperation({ summary: '测试' })\n  @Public()\n  @Get('test')\n  async getTest() {\n    const res = \"success\";\n    return res;\n  }\n\n  @ApiOperation({ summary: '获取YouTube授权URL' })\n  @Get('auth/url')\n  async getAuthUrl(\n    @GetToken() systemToken: TokenInfo,\n    @Query('mail') mail: string) {\n    if (!mail) {\n      throw new BadRequestException('邮箱参数不能为空');\n    }\n  // async getAuthUrl(@GetToken() token: TokenInfo) {\n  //   // mail = token.id\n  //   // if (!mail) {\n  //   //   throw new BadRequestException('邮箱参数不能为空');\n  //   // }\n\n    return this.youtubeAuthService.getAuthorizationUrl(mail, systemToken.id);\n  }\n\n  @ApiOperation({ summary: '处理YouTube授权回调' })\n  @Public()\n  @Get('auth/callback')\n  async handleAuthCallback(\n    // @GetToken() systemToken: TokenInfo,\n    @Query('code') code: string,\n    @Query('state') state: string,\n    // @Query('userId') userId: string,","sourceCodeStart":38,"sourceCodeEnd":74,"githubUrl":"https://github.com/yikart/AiToEarn/blob/d3aa8bea5b146a8675607cf0144d891aad3e9683/project/aitoearn-electron/server/src/modules/plat/youtube/youtube.controller.ts#L38-L74","documentation":"The GET /youtube/auth/url endpoint requires a 'mail' query parameter that identifies which user/email the YouTube OAuth authorization is for. NestJS BadRequestException (HTTP 400) is thrown when the mail parameter is absent or empty.","triggerScenarios":"Calling GET /api/plat/youtube/auth/url without ?mail= or with mail= (empty string), typically when constructing the authorization URL request programmatically and forgetting the query parameter.","commonSituations":"Frontend calls the endpoint after login but does not propagate the user's email; curl/Postman testing omits the query param; URL built with a template variable that was undefined and serialized as empty.","solutions":["Append the mail query parameter: GET /youtube/auth/url?mail=user@example.com","Verify the frontend reads the current user's email (not undefined) before calling the endpoint","Ensure the query string is properly encoded (encodeURIComponent for emails with special chars)"],"exampleFix":"// before\nconst res = await fetch('/api/plat/youtube/auth/url');\n// after\nconst res = await fetch(`/api/plat/youtube/auth/url?mail=${encodeURIComponent(userEmail)}`);","handlingStrategy":"validation","validationCode":"if (!mail || typeof mail !== 'string' || !mail.includes('@')) {\n  throw new Error('mail query parameter is required before calling /youtube/auth/url');\n}","typeGuard":"function hasMail(q: Record<string, unknown>): q is { mail: string } {\n  return typeof q.mail === 'string' && q.mail.trim().length > 0;\n}","tryCatchPattern":"try {\n  const { data } = await api.get('/plat/youtube/auth/url', { params: { mail: userEmail } });\n} catch (e) {\n  if (e.response?.status === 400 && String(e.response.data?.message).includes('邮箱参数')) {\n    // prompt user to provide/select an email and retry\n  }\n}","preventionTips":["Always read the authenticated user's email from session/token before building the request","encodeURIComponent the mail value","Add client-side form validation for required email","Keep integration docs listing required query params for the endpoint"],"tags":["http-400","validation","query-param","youtube"],"backgroundTag":"missing-query-parameter","analyzedSha":"d3aa8bea5b146a8675607cf0144d891aad3e9683","analyzedAt":"2026-08-31T14:19:24.185Z","schemaVersion":2},"datasetVersion":"2026-08-31T19:17:28.585Z"}