{"record":{"id":"8ab9a4b25de79be5","repo":"yikart/AiToEarn","slug":"error-response-data-error-message-8ab9a4","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":231,"sourceCode":"\n      const { data } = await firstValueFrom(\n        this.httpService.post(`${this.apiBaseUrl}/v2/post/publish/video/init/`, requestBody, {\n          headers: {\n            'Content-Type': 'application/json',\n            'Authorization': `Bearer ${accessToken}`\n          }\n        })\n      );\n      \n      if (!data.data.publish_id || !data.data.upload_url) {\n        throw new BadRequestException('初始化视频发布失败，缺少publish_id或upload_url');\n      }\n\n      return data.data;\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 videoBuffer 视频文件缓冲区\n   * @param initData 初始化返回的数据\n   * @returns 上传结果\n   */\n  async uploadVideo(\n    accessToken: string,\n    videoBuffer: Buffer,\n    initData?: any\n  ): Promise<any> {\n    try {\n      // 如果没有提供初始化数据，先进行初始化\n      if (!initData) {\n        // 计算视频大小并进行初始化","sourceCodeStart":213,"sourceCodeEnd":249,"githubUrl":"https://github.com/yikart/AiToEarn/blob/d3aa8bea5b146a8675607cf0144d891aad3e9683/project/aitoearn-electron/server/src/modules/plat/tiktok/tiktok.service.ts#L213-L249","documentation":"This BadRequestException is the catch-all rethrow of TikTokService.initVideoPublish: any exception raised while calling POST ${apiBaseUrl}/v2/post/publish/video/init/ — including the missing publish_id/upload_url guard (error 421) and any axios/TikTok API error — is caught, logged, and rethrown as BadRequestException('初始化视频发布失败: <TikTok error.message or axios message>'). It deliberately overwrites the original error's identity, so the missing-field BadRequestException from line 224 also emerges with this wrapper text.","triggerScenarios":"initVideoPublish's try block fails for any reason: (1) axios error from firstValueFrom (4xx/5xx from TikTok: invalid token, missing video.publish scope, invalid post_info/source_info values, file size outside limits); (2) the explicit '缺少publish_id或upload_url' BadRequestException thrown at line 224; (3) network failure where error.response is undefined so error.message (e.g. 'timeout of 10000ms exceeded') is interpolated.","commonSituations":"Developers hit this with expired/revoked OAuth tokens, apps whose video.publish scope isn't audit-approved, privacy_level values not permitted for the app tier, video_size/chunk_size/total_chunk_count inconsistencies in source_info, oversized title strings with hashtags exceeding TikTok limits, and confusingly also when the response genuinely lacks publish_id/upload_url because the inner guard's message gets swallowed by this wrapper.","solutions":["Read the logged detail ('初始化TikTok视频发布失败:' + error.response?.data) to distinguish an HTTP error from the local missing-field guard failure.","If the message says 缺少publish_id或upload_url, inspect the raw TikTok response for an error envelope returned with HTTP 200 (see solutions for error 421).","If it's a TikTok API error, fix per its code: refresh the access token, request/verify video.publish scope, or correct post_info/source_info fields.","Validate inputs before the call: videoSize > 0, chunkSize within 5–64MB, title length within TikTok's limit, privacy_level valid for app status.","Stop double-wrapping: rethrow the inner BadRequestException as-is so the specific cause ('缺少publish_id或upload_url') is not masked."],"exampleFix":"// before: every failure becomes the same generic message\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: preserve already-typed errors, wrap only upstream failures\n} catch (error) {\n  this.logger.error('初始化TikTok视频发布失败:', error.response?.data || error.message);\n  if (error instanceof BadRequestException) throw error;\n  const apiErr = error.response?.data?.error;\n  throw new BadRequestException(`初始化视频发布失败: [${apiErr?.code ?? 'UNKNOWN'}] ${apiErr?.message ?? error.message}`);\n}","handlingStrategy":"try-catch","validationCode":"function validatePublishInputs(accessToken: string, videoSize: number, videoInfo: { privacyStatus?: string; title?: string; tags?: string[] }) {\n  if (!accessToken) throw new Error('缺少 accessToken');\n  if (!Number.isFinite(videoSize) || videoSize <= 0) throw new Error('videoSize 非法');\n  const privacy = videoInfo.privacyStatus ?? 'PUBLIC';\n  const allowed = ['PUBLIC', 'SELF_ONLY', 'FRIENDS'];\n  if (!allowed.includes(privacy)) throw new Error(`非法 privacy_level: ${privacy}`);\n  const title = `${videoInfo.title ?? ''} ${(videoInfo.tags ?? []).map(t => '#' + t.replace(/^#/, '')).join(' ')}`;\n  if (title.length > 2200) throw new Error('标题（含 hashtag）超出 TikTok 长度限制');\n}","typeGuard":"function isAxiosUpstreamError(e: unknown): e is { isAxiosError: true; response?: { status: number; data: { error?: { code: string; message: string } } }; message: string } {\n  return typeof e === 'object' && e !== null && (e as any).isAxiosError === true;\n}","tryCatchPattern":"try {\n  const initData = await tiktokService.initVideoPublish(accessToken, videoSize, videoInfo);\n} catch (e) {\n  if (e instanceof BadRequestException && e.message.includes('缺少publish_id')) {\n    // local response-shape failure — inspect raw response, do not blindly retry\n  } else if (isAxiosUpstreamError(e) && e.response?.status === 401) {\n    // token invalid — refresh and retry once\n  } else if (isAxiosUpstreamError(e) && !e.response) {\n    // network error — retry with backoff\n  }\n  throw e;\n}","preventionTips":["Validate accessToken, videoSize, privacy_level and title length before calling initVideoPublish.","Refresh tokens before long publish pipelines; 401 mid-flow means re-auth, not retry.","Rethrow typed/local errors unchanged so specific causes are not masked by generic wrappers.","Record TikTok error.code from error.response.data.error in structured logs/alerts.","Verify app scope (video.publish) and production audit status before enabling direct publish."],"tags":["tiktok","api-error","video-publish","error-wrapping","nestjs"],"backgroundTag":"upstream-api-request-failed","analyzedSha":"d3aa8bea5b146a8675607cf0144d891aad3e9683","analyzedAt":"2026-08-31T14:19:24.185Z","schemaVersion":2},"datasetVersion":"2026-08-31T19:17:28.585Z"}