{"record":{"id":"0f067415b5825665","repo":"yikart/AiToEarn","slug":"youtube-api","errorCode":null,"errorMessage":"YouTube API请求失败","messagePattern":"YouTube API请求失败","errorType":"exception","errorClass":"BadRequestException","httpStatus":400,"severity":"error","filePath":"project/aitoearn-electron/server/src/modules/plat/youtube/youtube.service.ts","lineNumber":1312,"sourceCode":"   * 处理API错误\n   * @param error 错误对象\n   */\n  private handleApiError(error: any) {\n    console.error('YouTube API Error:', error);\n\n    if (error.response) {\n      // API响应错误\n      const { status, data } = error.response;\n      if (status === 401) {\n        throw new BadRequestException('授权已过期，请重新授权');\n      } else if (status === 403) {\n        throw new BadRequestException('权限不足，无法执行此操作');\n      } else if (data && data.error && data.error.message) {\n        throw new BadRequestException(`YouTube API错误: ${data.error.message}`);\n      }\n    }\n\n    throw new BadRequestException('YouTube API请求失败');\n  }\n\n}\n\n","sourceCodeStart":1294,"sourceCodeEnd":1317,"githubUrl":"https://github.com/yikart/AiToEarn/blob/d3aa8bea5b146a8675607cf0144d891aad3e9683/project/aitoearn-electron/server/src/modules/plat/youtube/youtube.service.ts#L1294-L1317","documentation":"The handler's fallback: if error.response is absent (network failure, timeout, DNS error, request never reached Google) or status is 401/403 with no parsable body and no data.error.message, the service throws BadRequestException('YouTube API请求失败'). It's the catch-all meaning 'the call failed for an unclassified reason'.","triggerScenarios":"error.response is undefined: connection refused/timeout, proxy blocking googleapis.com, ECONNRESET, TLS issues, or offline server; also 401/403 responses whose body lacks error.error.message fall through here.","commonSituations":"Corporate proxy/firewall blocking outbound HTTPS to www.googleapis.com; server in a region without YouTube API access; DNS misconfiguration; axios misconfiguration (no adapter/timeout); error object not an AxiosError at all.","solutions":["Log the raw error (error.code, error.cause, error.message) — absence of error.response means the request never completed.","Check outbound network/DNS/proxy access to www.googleapis.com:443 from the server.","Add timeouts and classify ECONNABORTED/ENOTFOUND/EAI_AGAIN codes distinctly from API errors.","Ensure the axios error is actually passed to this handler (not swallowed or wrapped elsewhere).","Implement retry with exponential backoff for transient network codes."],"exampleFix":"// before\nconsole.error('YouTube API Error:', error);\n// after\nif (!error.response) {\n  console.error('Network-level failure:', { code: error.code, message: error.message, cause: error.cause });\n  throw new BadRequestException(`YouTube 网络请求失败: ${error.code ?? 'UNKNOWN'}`);\n}","handlingStrategy":"try-catch","validationCode":"const reachable = await fetch('https://www.googleapis.com/discovery/v1/apis', { signal: AbortSignal.timeout(5000) }).then(r => r.ok).catch(() => false);\nif (!reachable) throw new Error('googleapis.com unreachable from this host');","typeGuard":"function isNetworkError(e: unknown): boolean {\n  const anyE = e as any;\n  return !anyE?.response && ['ECONNREFUSED','ETIMEDOUT','ECONNRESET','ENOTFOUND','EAI_AGAIN','ECONNABORTED'].includes(anyE?.code ?? '');\n}","tryCatchPattern":"try {\n  return await callYouTubeApi();\n} catch (e) {\n  if (isNetworkError(e)) {\n    await sleep(backoff(attempt)); // retry with exponential backoff up to N times\n    return callYouTubeApi();\n  }\n  throw e;\n}","preventionTips":["Verify server egress to www.googleapis.com:443 (proxy/firewall/region allowlist).","Set explicit axios timeouts and classify error.code values.","Retry transient network codes with exponential backoff and jitter.","Centralize error mapping so unclassified failures log code/cause instead of a bare message."],"tags":["network","youtube-api","unclassified-error","connectivity"],"backgroundTag":"network-request-failed","analyzedSha":"d3aa8bea5b146a8675607cf0144d891aad3e9683","analyzedAt":"2026-08-31T14:19:24.185Z","schemaVersion":2},"datasetVersion":"2026-08-31T19:17:28.585Z"}