{"record":{"id":"872e91e9e45b77e9","repo":"yikart/AiToEarn","slug":"error-response-data-error-message-872e91","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":451,"sourceCode":"                'Content-Type': contentType,\n                'Content-Length': chunkLength.toString(),\n                'Content-Range': `bytes ${start}-${end-1}/${totalSize}`,\n                'Authorization': `Bearer ${accessToken}`\n              }\n            })\n          );\n          \n          lastResponse = data;\n        }\n        \n        return {\n          publish_id,\n          ...lastResponse?.data,\n        };\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 publishId 发布ID\n   * @returns 发布状态信息\n   */\n  async checkPublishStatus(\n    accessToken: string,\n    publishId: string\n  ): Promise<any> {\n    try {\n      const { data } = await firstValueFrom(\n        this.httpService.post(\n          `${this.apiBaseUrl}/v2/post/publish/status/fetch/`, \n          { publish_id: publishId },","sourceCodeStart":433,"sourceCodeEnd":469,"githubUrl":"https://github.com/yikart/AiToEarn/blob/d3aa8bea5b146a8675607cf0144d891aad3e9683/project/aitoearn-electron/server/src/modules/plat/tiktok/tiktok.service.ts#L433-L469","documentation":"This is the catch-all wrapper in directUploadVideo: any failure while PUT-ing the video (single-shot or 10MB chunks) to TikTok's upload_url — HTTP errors, expired URL, token rejection, network failure — is logged and re-thrown as BadRequestException('直接上传视频失败: ...') at tiktok.service.ts:451. The embedded detail comes from error.response.data.error.message (Axios) or error.message.","triggerScenarios":"Any of: the PUT to upload_url returns 4xx/5xx (expired signature, invalid token, wrong content type), a >10MB video's Content-Range chunk is rejected, connection reset mid-PUT, or the internal missing-field throw from line 387 being re-caught here and re-wrapped.","commonSituations":"Upload URL TTL expired while preparing a large file, access token expired or wrong environment, TikTok rejecting non-mp4 or oversized files, unstable network on large uploads, and double-wrapping masking the original root cause.","solutions":["Inspect the logged '直接上传TikTok视频失败' body for TikTok's error code; on auth errors re-authorize and restart from the init step.","Re-run the init call to get a fresh upload_url if the error suggests an expired/invalid URL, then retry.","Confirm the file is mp4 and within TikTok size limits; convert/transcode if not.","Add retry-with-backoff for transient network errors (5xx/timeouts) around each PUT, and avoid re-wrapping internal BadRequestExceptions so root causes stay visible."],"exampleFix":"// before\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// after\n} catch (error) {\n  this.logger.error('直接上传TikTok视频失败:', error.response?.data || error.message);\n  if (error instanceof BadRequestException) throw error;\n  throw new BadRequestException(`直接上传视频失败: ${error.response?.data?.error?.message || error.message}`);\n}","handlingStrategy":"try-catch","validationCode":"if (!initData?.upload_url) throw new Error('missing upload_url');\nconst MAX = 2 * 1024 * 1024 * 1024; // check TikTok's documented limit\nif (videoBuffer.length > MAX) throw new Error('video exceeds TikTok size limit');\nif (!videoBuffer.slice(0, 12).toString('latin1').includes('ftyp')) {\n  this.logger.warn('videoBuffer may not be a valid MP4');\n}","typeGuard":"function isMp4Buffer(v) {\n  return Buffer.isBuffer(v) && v.length > 11\n    && v.readUInt32BE(4) === 0x66747970; // 'ftyp' box\n}","tryCatchPattern":"try {\n  const result = await directUploadVideo(accessToken, videoBuffer, initData);\n} catch (e) {\n  const detail = /直接上传视频失败: (.*)/.exec(e.message)?.[1] ?? e.message;\n  if (detail.includes('401') || detail.toLowerCase().includes('unauthorized')) {\n    // refresh token then re-init and retry\n  } else if (/expired|invalid.*url/i.test(detail)) {\n    // re-init for a fresh upload_url\n  } else throw e;\n}","preventionTips":["Check upload_url freshness right before the PUT; re-init if the gap since init is more than a few minutes.","Verify access token validity (and environment match) before starting the upload.","Pre-transcode to mp4 within TikTok limits to avoid mid-upload rejection.","Distinguish token/URL errors from transient network errors so retries target the right cause."],"tags":["tiktok","upload","http-error","network"],"backgroundTag":"chunked-upload-failed","analyzedSha":"d3aa8bea5b146a8675607cf0144d891aad3e9683","analyzedAt":"2026-08-31T14:19:24.185Z","schemaVersion":2},"datasetVersion":"2026-08-31T19:17:28.585Z"}