{"record":{"id":"dfbc4d4c5acf3a75","repo":"yikart/AiToEarn","slug":"error-response-data-error-message-dfbc4d","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":608,"sourceCode":"        throw new BadRequestException('视频发布状态检查超时，请稍后在TikTok应用中查看发布状态');\n      }\n      \n      // 更新发布记录为成功状态\n      const videoId = finalStatus.video_id || finalStatus.id || publish_id;\n      await this.pubRecordModel.findByIdAndUpdate(pubRecord._id, {\n        status: PubStatus.RELEASED,\n        resourceId: videoId,\n        updateTime: new Date()\n      });\n      \n      return {\n        ...finalStatus,\n        resourceId: videoId,\n        publish_id,\n      };\n    } catch (error) {\n      this.logger.error('三步式视频上传发布失败:', 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 videoDto 视频发布参数\n   * @param uploadResult 上传结果，包含视频ID和初始化数据\n   * @returns 发布结果\n   */\n  async publishVideo(\n    accessToken: string,\n    userId: string,\n    accountId: string,\n    videoDto: CreateVideoDto,\n    uploadResult: any","sourceCodeStart":590,"sourceCodeEnd":626,"githubUrl":"https://github.com/yikart/AiToEarn/blob/d3aa8bea5b146a8675607cf0144d891aad3e9683/project/aitoearn-electron/server/src/modules/plat/tiktok/tiktok.service.ts#L590-L626","documentation":"The outer catch-all of uploadAndPublishVideo (tiktok.service.ts:606-609). Any exception from the three-step flow (init, direct upload, status polling, DB writes) is re-wrapped as BadRequestException('视频上传发布失败: ...') with the deepest TikTok API error message or the original error message. It often wraps the other errors in this file (430-432), so the interesting cause is in the chained message.","triggerScenarios":"Any failure inside the try block: initVideoPublish/directUploadVideo/checkPublishStatus throwing AxiosErrors (4xx/5xx from TikTok), the invariant checks at lines 522/575/590 throwing, or MongoDB errors on pubRecordModel writes; error.response?.data?.error?.message is used when it's an HTTP error, error.message otherwise.","commonSituations":"Expired/revoked access tokens (401), missing publish scope (403), rate limiting (429), network failures during the chunked upload, Mongo connection issues, or simply seeing this generic wrapper when the real cause was error 431/432.","solutions":["Check the server log line '三步式视频上传发布失败:' — it prints error.response?.data with TikTok's structured error (code, message, log_id).","If the wrapped message is about auth, refresh the access token and ensure video.publish scope.","If it's 429/rate-limited, back off and retry later; TikTok caps status checks and uploads per hour.","If it's a wrapped 522/431/432 error, follow that specific error's remediation.","Preserve the original error stack (rethrow with { cause: error }) instead of flattening to a string for easier debugging."],"exampleFix":"// before\nthrow new BadRequestException(`视频上传发布失败: ${error.response?.data?.error?.message || error.message}`);\n// after\nthrow new BadRequestException(`视频上传发布失败: ${error.response?.data?.error?.message || error.message}`, {\n  cause: error,\n});","handlingStrategy":"try-catch","validationCode":"// Before calling, ensure prerequisites hold:\nif (!accessToken) throw new Error('missing TikTok access token');\nif (!videoBuffer?.length) throw new Error('empty video buffer');\nif (videoInfo.description && videoInfo.description.length > 2200) throw new Error('description too long');","typeGuard":"function isAxiosTikTokError(e: unknown): e is { response: { status: number; data: { error?: { code?: string; message?: string; log_id?: string } } } } {\n  return !!(e as any)?.response?.data;\n}","tryCatchPattern":"try {\n  return await tiktokService.uploadAndPublishVideo(token, userId, accountId, buffer, info);\n} catch (e) {\n  const apiErr = (e as any).cause?.response?.data?.error;\n  logger.error('uploadAndPublish failed', { apiErr, msg: e.message });\n  if (apiErr?.code === 'access_token_invalid') token = await refreshTikTokToken(accountId);\n  throw new PublishError(e.message, { cause: (e as any).cause ?? e });\n}","preventionTips":["Refresh tokens proactively; TikTok access tokens expire in 24h.","Check the inner cause/origin log line, not just the generic wrapper message.","Handle 429 with exponential backoff at the caller level.","Log TikTok's log_id field — support can trace failures with it."],"tags":["tiktok","error-wrapper","upload","http-client"],"backgroundTag":"wrapped-upstream-error","analyzedSha":"d3aa8bea5b146a8675607cf0144d891aad3e9683","analyzedAt":"2026-08-31T14:19:24.185Z","schemaVersion":2},"datasetVersion":"2026-08-31T19:17:28.585Z"}