{"record":{"id":"bb3ea30423647eb5","repo":"yikart/AiToEarn","slug":"channelplatformmediaprocessingfailed-bb3ea3","errorCode":"ChannelPlatformMediaProcessingFailed","errorMessage":"ChannelPlatformMediaProcessingFailed","messagePattern":"ChannelPlatformMediaProcessingFailed","errorType":"error_code","errorClass":"AppException","httpStatus":null,"severity":"error","filePath":"project/aitoearn-backend/apps/aitoearn-server/src/core/channels/platforms/douyin/douyin.service.ts","lineNumber":492,"sourceCode":"    accessToken: string,\n    openId: string,\n    videoBuffer: Buffer,\n    filename: string,\n  ): Promise<{ videoId: string }> {\n    const formData = new FormData()\n    formData.append('video', new Blob([new Uint8Array(videoBuffer)]), filename)\n\n    const response = await this.http.post<DouyinApiResponse<DouyinVideoUploadResponse>>(\n      '/api/douyin/v1/video/upload_video/',\n      formData,\n      {\n        params: { open_id: openId },\n        headers: { 'access-token': accessToken },\n      },\n    )\n    const videoId = response.data.data.video?.video_id\n    if (!videoId) {\n      throw new AppException(ResponseCode.ChannelPlatformMediaProcessingFailed, { platform: AccountType.Douyin, field: 'video_id', reasonCode: 'missing_platform_field' })\n    }\n\n    return { videoId }\n  }\n\n  async uploadImage(\n    accessToken: string,\n    openId: string,\n    imageBuffer: Buffer,\n    filename: string,\n  ): Promise<{ imageId: string }> {\n    const formData = new FormData()\n    formData.append('image', new Blob([new Uint8Array(imageBuffer)]), filename)\n\n    const response = await this.http.post<DouyinApiResponse<{ image?: { image_id?: string } }>>(\n      '/api/douyin/v1/video/upload_image/',\n      formData,\n      {","sourceCodeStart":474,"sourceCodeEnd":510,"githubUrl":"https://github.com/yikart/AiToEarn/blob/d3aa8bea5b146a8675607cf0144d891aad3e9683/project/aitoearn-backend/apps/aitoearn-server/src/core/channels/platforms/douyin/douyin.service.ts#L474-L510","documentation":"ChannelPlatformMediaProcessingFailed (field video_id) is thrown by DouyinService.uploadVideo when the Douyin video upload API responds without data.video.video_id. The upload request itself completed at HTTP level, but the platform did not return the identifier needed for subsequent createVideo/publish calls, so the service flags media processing as failed.","triggerScenarios":"Calling uploadVideo (e.g. via videoId()) with an open_id/accessToken pair where the multipart upload returns an error envelope: invalid or expired access token, open_id not belonging to the authorized user, file exceeding Douyin size/duration limits, or platform error response with no video object.","commonSituations":"Access token expired after the user deauthorized the app; uploading a video over Douyin's limits (e.g. >128MB or wrong codec); account not granted video publish scope; transient Douyin incident returning empty data.","solutions":["Check the upload response envelope for a platform error code (e.g. access_token invalid) and refresh the user access token if expired.","Validate the video file against Douyin limits (size, duration, mp4/H.264) before uploading.","Confirm the open_id matches the account bound to the accessToken and that publish scope was granted.","Retry the upload; if it persists with a valid token, capture the raw response and contact Douyin platform support."],"exampleFix":"// before\nconst { videoId } = await douyinService.uploadVideo(openId, token, badFile) // no video_id returned\n// after\n// validate file then upload\nconst ok = file.size <= 128 * 1024 * 1024 && file.mimetype === 'video/mp4'\nif (!ok) throw new Error('file exceeds Douyin limits')\nconst { videoId } = await douyinService.uploadVideo(openId, freshToken, file) // returns video_id","handlingStrategy":"validation","validationCode":"function assertDouyinVideoFile(f: { size: number; mimetype: string }) {\n  const MAX = 128 * 1024 * 1024\n  if (f.size > MAX) throw new Error('video exceeds Douyin 128MB limit')\n  if (!['video/mp4'].includes(f.mimetype)) throw new Error('only mp4 supported')\n}","typeGuard":"const hasVideoId = (r: unknown): r is { data: { data: { video: { video_id: string } } } } =>\n  !!r && typeof r === 'object' && typeof (r as any).data?.data?.video?.video_id === 'string'","tryCatchPattern":"try {\n  const { videoId } = await douyinService.uploadVideo(openId, accessToken, file)\n} catch (err) {\n  if (err instanceof AppException && err.code === ResponseCode.ChannelPlatformMediaProcessingFailed && err.data?.field === 'video_id') {\n    // refresh user token / revalidate file, then retry once\n  } else throw err\n}","preventionTips":["Check token validity and refresh before long upload batches","Enforce Douyin file limits (size, duration, format) client-side before upload","Confirm open_id and accessToken come from the same authorized account","Retry transient failures with exponential backoff instead of failing the whole publish"],"tags":["douyin","media-upload","video","missing-field"],"backgroundTag":"media-upload-failed","analyzedSha":"d3aa8bea5b146a8675607cf0144d891aad3e9683","analyzedAt":"2026-08-31T14:19:24.185Z","schemaVersion":2},"datasetVersion":"2026-08-31T19:17:28.585Z"}