{"record":{"id":"6592e4caacf13a1d","repo":"yikart/AiToEarn","slug":"error-response-data-error-message-6592e4","errorCode":null,"errorMessage":"获取视频列表失败: ${error.response?.data?.error?.message || error.message}","messagePattern":"获取视频列表失败: (.+?)","errorType":"exception","errorClass":"BadRequestException","httpStatus":400,"severity":"error","filePath":"project/aitoearn-electron/server/src/modules/plat/tiktok/tiktok.service.ts","lineNumber":74,"sourceCode":"      };\n\n      if (cursor) {\n        params.cursor = cursor;\n      }\n\n      const { data } = await firstValueFrom(\n        this.httpService.get(`${this.apiBaseUrl}/v2/video/list`, {\n          params,\n          headers: {\n            'Authorization': `Bearer ${accessToken}`\n          }\n        })\n      );\n\n      return data.data;\n    } catch (error) {\n      this.logger.error('获取TikTok视频列表失败:', error.response?.data || error.message);\n      throw new BadRequestException(`获取视频列表失败: ${error.response?.data?.error?.message || error.message}`);\n    }\n  }\n\n  /**\n   * 获取视频详情\n   * @param accessToken 访问令牌\n   * @param videoId 视频ID\n   * @returns 视频详情\n   */\n  async getVideoDetail(\n    accessToken: string,\n    videoId: string\n  ): Promise<any> {\n    try {\n      const { data } = await firstValueFrom(\n        this.httpService.get(`${this.apiBaseUrl}/v2/video/info/`, {\n          params: {\n            fields: 'id,create_time,video_description,duration,height,width,share_count,comment_count,like_count,view_count,title,embed_link,embed_html,thumbnail_url',","sourceCodeStart":56,"sourceCodeEnd":92,"githubUrl":"https://github.com/yikart/AiToEarn/blob/d3aa8bea5b146a8675607cf0144d891aad3e9683/project/aitoearn-electron/server/src/modules/plat/tiktok/tiktok.service.ts#L56-L92","documentation":"getUserVideos wraps any failure from TikTok's video-list API (or the local HTTP request) in a 400 BadRequestException whose message embeds TikTok's error.message (error.response.data.error.message) or the generic axios error message. The root cause is upstream — invalid/expired token, bad cursor, TikTok API rejection — re-thrown under a uniform message.","triggerScenarios":"Calling getUserVideos with an expired/revoked access token, an invalid cursor/fields parameter, or while TikTok returns a non-2xx response; also on network failure to TikTok.","commonSituations":"Long-lived integrations whose TikTok OAuth token expired; rate limiting from TikTok; wrong API version/base URL in the service config; sandbox tokens hitting production endpoints.","solutions":["Read the embedded error.message from the exception response to identify the TikTok-side cause.","Refresh the TikTok access token via the auth service (getUserAccessToken / refresh flow) and retry.","Validate cursor, max_count, and fields params against TikTok's video-list API contract.","Add retry with backoff for transient TikTok/network failures and rate limits."],"exampleFix":"// before\nconst videos = await tiktokService.getUserVideos(token, cursor);\n// after\ntry {\n  const videos = await tiktokService.getUserVideos(token, cursor);\n} catch (e) {\n  if (String(e.message).includes('access_token')) {\n    token = await refreshAccessToken(accountId);\n    return retry();\n  }\n  throw e;\n}","handlingStrategy":"try-catch","validationCode":"if (!accessToken) throw new Error('No TikTok access token for this account — reconnect the account first');\nconst expiresAt = getTokenExpiresAt(accountId);\nif (expiresAt && expiresAt <= Date.now() + 60_000) await refreshAccessToken(accountId);","typeGuard":"function isTokenError(msg: string): boolean {\n  return /access_token|token.*invalid|token.*expired/i.test(msg);\n}","tryCatchPattern":"try {\n  return await api.post('/tiktok/video/list', { accountId, cursor });\n} catch (e) {\n  const msg = e.response?.data?.message || e.message;\n  if (isTokenError(msg)) {\n    await refreshAccessToken(accountId);\n    return api.post('/tiktok/video/list', { accountId, cursor });\n  }\n  if (e.response?.status === 429) await sleep(backoff());\n  throw e;\n}","preventionTips":["Proactively refresh TikTok tokens before expiry instead of on failure.","Parse the embedded Chinese-prefixed message to route token vs param vs rate-limit failures.","Implement exponential backoff for 429/network errors from TikTok.","Alert on repeated wrapped failures per account to catch expired tokens early."],"tags":["tiktok","api-error","http","token-expired","wrapped-error"],"backgroundTag":"upstream-api-error-wrapped","analyzedSha":"d3aa8bea5b146a8675607cf0144d891aad3e9683","analyzedAt":"2026-08-31T14:19:24.185Z","schemaVersion":2},"datasetVersion":"2026-08-31T19:17:28.585Z"}