{"record":{"id":"cd3480fe0a5a80a8","repo":"yikart/AiToEarn","slug":"error-response-data-error-message-cd3480","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":705,"sourceCode":"          status: PubStatus.RELEASED,\n          remoteId: data.data.share_id || videoId,\n          updateTime: new Date()\n        });\n\n        return data.data;\n      } catch (error) {\n        // 更新发布记录为失败状态\n        await this.pubRecordModel.findByIdAndUpdate(pubRecord._id, {\n          status: PubStatus.FAIL,\n          failReason: error.response?.data?.error?.message || error.message,\n          updateTime: new Date()\n        });\n\n        throw error;\n      }\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 deleteVideo(\n    accessToken: string,\n    videoId: string\n  ): Promise<any> {\n    try {\n      const { data } = await firstValueFrom(\n        this.httpService.post(`${this.apiBaseUrl}/v2/video/delete/`, {\n          video_id: videoId\n        }, {","sourceCodeStart":687,"sourceCodeEnd":723,"githubUrl":"https://github.com/yikart/AiToEarn/blob/d3aa8bea5b146a8675607cf0144d891aad3e9683/project/aitoearn-electron/server/src/modules/plat/tiktok/tiktok.service.ts#L687-L723","documentation":"The outer catch of publishVideo (tiktok.service.ts:703-706). Any failure in the legacy publish flow — the video_id check (434), building params, the POST to the publish URL, or the Mongo record writes — is re-wrapped as BadRequestException('发布视频失败: ...') carrying the TikTok API error message or the original error message. The inner block already marks the publish record FAIL before rethrowing.","triggerScenarios":"POST to /v2/video/publish/ (or init_data.publish_url) returns 4xx/5xx: invalid or expired access token, missing video.publish scope, invalid video_id (expired — TikTok video ids from upload expire quickly), text length/privacy_level violations, or DB write failures.","commonSituations":"Waiting too long between upload and publish so the uploaded video expires; app not approved for video publishing; text exceeding TikTok's character limits; using the wrong environment's token (China vs international endpoints).","solutions":["Check the '发布TikTok视频失败:' log line for error.response?.data — it contains TikTok's error code and log_id.","Publish promptly after upload; if the error says the video is invalid/expired, re-upload and publish immediately.","Refresh the access token and confirm the video.publish scope is granted.","Validate params.text length and privacy_level values against TikTok constraints before the call.","Check the publish record in Mongo — failReason was written there with the same message."],"exampleFix":"null","handlingStrategy":"try-catch","validationCode":"const vid = uploadResult?.video_id ?? uploadResult?.init_data?.video_id;\nif (!accessToken || !vid) throw new Error('token or video_id missing before publish');\nconst text = dto.description ?? '';\nif (text.length + (dto.hashtags?.length ?? 0) > 2200) throw new Error('publish text exceeds TikTok limit');","typeGuard":"function isUpstreamHttpError(e: unknown): e is { response: { status: number; data: { error?: { message?: string } } } } {\n  const o = e as any;\n  return typeof o?.response?.status === 'number';\n}","tryCatchPattern":"try {\n  await tiktokService.publishVideo(token, userId, accountId, dto, uploadResult);\n} catch (e) {\n  if (isUpstreamHttpError(e) && e.response.status === 401) {\n    // refresh token then retry once\n  } else if (/invalid.*video/.test(e.message)) {\n    // video expired: re-upload then publish immediately\n  } else throw e;\n}","preventionTips":["Publish within seconds of upload — TikTok upload ids expire quickly.","Verify the token's scopes include video.publish before each publish session.","Clamp text length and hashtag count before sending.","Check the Mongo publish record's failReason — it mirrors this error."],"tags":["tiktok","publish-failed","error-wrapper","http-client"],"backgroundTag":"wrapped-upstream-error","analyzedSha":"d3aa8bea5b146a8675607cf0144d891aad3e9683","analyzedAt":"2026-08-31T14:19:24.185Z","schemaVersion":2},"datasetVersion":"2026-08-31T19:17:28.585Z"}