{"record":{"id":"3913bfdd83363887","repo":"yikart/AiToEarn","slug":"error-response-data-error-message-3913bf","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":840,"sourceCode":"    commentId: string\n  ): Promise<any> {\n    try {\n      const { data } = await firstValueFrom(\n        this.httpService.post(`${this.apiBaseUrl}/v2/comment/delete/`, {\n          video_id: videoId,\n          comment_id: commentId\n        }, {\n          headers: {\n            'Content-Type': 'application/json',\n            'Authorization': `Bearer ${accessToken}`\n          }\n        })\n      );\n\n      return { success: true };\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 userId 用户ID\n   * @param accountId TikTok账号ID\n   * @param videoId 视频ID\n   * @returns 点赞结果\n   */\n  async likeVideo(\n    accessToken: string,\n    userId: string,\n    accountId: string,\n    videoId: string\n  ): Promise<any> {\n    try {","sourceCodeStart":822,"sourceCodeEnd":858,"githubUrl":"https://github.com/yikart/AiToEarn/blob/d3aa8bea5b146a8675607cf0144d891aad3e9683/project/aitoearn-electron/server/src/modules/plat/tiktok/tiktok.service.ts#L822-L858","documentation":"The catch-all of deleteComment (tiktok.service.ts:838-841). The POST to /v2/comment/delete/ failed and is re-wrapped as BadRequestException('删除评论失败: ...') with TikTok's error message when available. Deleting a comment requires it to belong to the token's user (as author or video owner) and the comment management scope.","triggerScenarios":"POST /v2/comment/delete/ returns 4xx/5xx: comment_id already deleted or not owned by the user, missing comment scope, video_id/comment_id mismatch, invalid or expired token, or network/5xx failure.","commonSituations":"Retrying deletion of an already-deleted comment; deleting a comment on someone else's video that the app user didn't author; stale comment_ids cached locally after the comment was removed; token refresh losing scopes.","solutions":["Check the '删除TikTok评论失败:' log for TikTok's error code distinguishing not-found from permission errors.","Treat 'comment not found' as already-deleted success to make deletion idempotent.","Verify the app user authored the comment (or owns the video) — other comments cannot be deleted.","Refresh the access token if the error is auth-related and confirm comment scopes are retained.","Retry with backoff only for 5xx/network errors, not for 4xx permission errors."],"exampleFix":"// before\nthrow new BadRequestException(`删除评论失败: ${...}`);\n// after\nconst code = error.response?.data?.error?.code;\nif (code === 'comment_not_found' || code === 'invalid_comment_id') {\n  return { success: true, alreadyDeleted: true };\n}\nthrow new BadRequestException(`删除评论失败: ${...}`);","handlingStrategy":"try-catch","validationCode":"if (!videoId || !commentId) throw new Error('videoId and commentId are required');","typeGuard":"null","tryCatchPattern":"try {\n  await tiktokService.deleteComment(token, videoId, commentId);\n} catch (e) {\n  if (/not (found|exist)|already.*deleted|invalid_comment/i.test(e.message)) {\n    return; // treat as already deleted\n  } else if ((e as any).response?.status === 403) {\n    // comment not owned by user — surface 'cannot delete others' comments'\n  } else throw e;\n}","preventionTips":["Make delete flows idempotent — comments are often deleted twice in UI retry loops.","Only attempt deletion of comments authored by the app user or on their videos.","Purge cached comment_ids after successful deletion.","Confirm comment scopes survive token refreshes."],"tags":["tiktok","comments","delete","http-client"],"backgroundTag":"wrapped-upstream-error","analyzedSha":"d3aa8bea5b146a8675607cf0144d891aad3e9683","analyzedAt":"2026-08-31T14:19:24.185Z","schemaVersion":2},"datasetVersion":"2026-08-31T19:17:28.585Z"}