{"record":{"id":"4437f3384105d40b","repo":"yikart/AiToEarn","slug":"url","errorCode":null,"errorMessage":"无法生成授权URL","messagePattern":"无法生成授权URL","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"project/aitoearn-electron/server/src/modules/plat/youtube/youtube.auth.service.ts","lineNumber":120,"sourceCode":"      const params = new URLSearchParams({\n        scope: youtubeScopes.join(\" \"),\n        access_type: \"offline\",\n        include_granted_scopes: \"true\",\n        response_type: \"code\",\n        state: encodedState,\n        redirect_uri: `${this.webRenderBaseUrl}/api/plat/youtube/auth/callback`,\n        client_id: this.webClientId,\n        prompt: \"consent\",  // 强制要求用户确认授权，以便我们能够获取refresh_token\n        // login_hint: userId,\n      });\n\n      const authUrl = new URL('https://accounts.google.com/o/oauth2/v2/auth');\n      authUrl.search = params.toString();\n\n      return {url: authUrl.toString()};\n    } catch (error) {\n      console.error('Error generating auth URL:', error);\n      throw new Error('无法生成授权URL');\n    }\n  }\n\n  /**\n   * 获取用户的YouTube访问令牌\n   * @param accountId 账号ID\n   * @returns 访问令牌\n   */\n  async getUserAccessToken(accountId: string): Promise<string> {\n    console.log(\"accountId:--\", accountId);\n    const accountTokenInfo = await this.AccountTokenModel.findOne({accountId: accountId});\n\n    // if (!res) return '';\n\n    // // 剩余时间\n    // const overTime = res.expires_in;\n\n    // if (overTime < 60 * 60 && overTime > 0) {","sourceCodeStart":102,"sourceCodeEnd":138,"githubUrl":"https://github.com/yikart/AiToEarn/blob/d3aa8bea5b146a8675607cf0144d891aad3e9683/project/aitoearn-electron/server/src/modules/plat/youtube/youtube.auth.service.ts#L102-L138","documentation":"getAuthorizationUrl builds the Google OAuth2 consent URL using GOOGLE_CONFIG.WEB_CLIENT_ID and WEB_RENDER_URL from ConfigService, then returns {url}. Any exception during construction (undefined config values producing invalid URL, redis service errors when storing the state key) is replaced by a generic Error('无法生成授权URL'), discarding the original cause from the thrown message.","triggerScenarios":"GOOGLE_CONFIG.WEB_CLIENT_ID or WEB_RENDER_URL missing/undefined in env config, making the URLSearchParams/URL build fail or produce a client_id=undefined URL; the redisService.setKey call for youtube:state:<userId>:<state> throws (Redis down); an invalid mail/userId argument.","commonSituations":"Deploying without the GOOGLE_CONFIG nested env block configured; Redis connection failure; wrong env file loaded in the electron server; typos in config keys so ConfigService.get returns undefined.","solutions":["Check that GOOGLE_CONFIG.WEB_CLIENT_ID and GOOGLE_CONFIG.WEB_RENDER_URL are set in the server env before calling this method.","Verify Redis is reachable, since the state key write happens before URL building.","Improve the catch to rethrow the original error (throw new Error(`无法生成授权URL: ${error.message}`)) to expose the root cause.","Validate the generated URL with new URL() and assert client_id is a non-empty string.","Confirm the env file loaded by ConfigService matches the running environment (dev vs prod)."],"exampleFix":"// before\n} catch (error) {\n  console.error('Error generating auth URL:', error);\n  throw new Error('无法生成授权URL');\n}\n// after\n} catch (error) {\n  console.error('Error generating auth URL:', error);\n  throw new Error(`无法生成授权URL: ${(error as Error).message}`);\n}","handlingStrategy":"validation","validationCode":"function assertGoogleConfig(config: ConfigService) {\n  const clientId = config.get<string>('GOOGLE_CONFIG.WEB_CLIENT_ID')\n  const baseUrl = config.get<string>('GOOGLE_CONFIG.WEB_RENDER_URL')\n  if (!clientId || !baseUrl) throw new Error('GOOGLE_CONFIG.WEB_CLIENT_ID / WEB_RENDER_URL not configured')\n  new URL(baseUrl) // throws on malformed base URL\n}","typeGuard":"const hasGoogleConfig = (c: unknown): c is { WEB_CLIENT_ID: string; WEB_RENDER_URL: string } =>\n  typeof c === 'object' && c !== null &&\n  typeof (c as any).WEB_CLIENT_ID === 'string' && (c as any).WEB_CLIENT_ID.length > 0 &&\n  typeof (c as any).WEB_RENDER_URL === 'string'","tryCatchPattern":"try {\n  const { url } = await youtubeAuthService.getAuthorizationUrl(mail, userId)\n} catch (e) {\n  if (e.message === '无法生成授权URL') {\n    // root cause is hidden; check GOOGLE_CONFIG env and Redis connectivity, then fail fast\n    throw new Error('YouTube OAuth config missing or Redis unavailable')\n  }\n  throw e\n}","preventionTips":["Add a startup config validator that fails fast when GOOGLE_CONFIG keys are absent","Monitor Redis health; the state key write precedes URL generation","Load and verify the correct env file per environment","Include the original error message when rethrowing wrapped errors"],"tags":["oauth","google","configuration","redis"],"backgroundTag":"missing-env-var","analyzedSha":"d3aa8bea5b146a8675607cf0144d891aad3e9683","analyzedAt":"2026-08-31T14:19:24.185Z","schemaVersion":2},"datasetVersion":"2026-08-31T19:17:28.585Z"}