{"record":{"id":"9ca6a0e772158ca7","repo":"yikart/AiToEarn","slug":"response-status","errorCode":null,"errorMessage":"下载失败: ${response.status}","messagePattern":"下载失败: \\$\\{response\\.status\\}","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"warning","filePath":"project/aitoearn-backend/apps/aitoearn-ai/src/core/agent/mcp/volcengine/volcengine.utils.ts","lineNumber":161,"sourceCode":"    const urlAuthPrimaryKey = config.ai.volcengine?.urlAuthPrimaryKey\n\n    if (!playbackBaseUrl || !urlAuthPrimaryKey) {\n      logger.warn({ fileName }, 'volcengine 未配置，跳过播放 URL 拼接')\n      return undefined\n    }\n\n    const normalizedFileName = fileName.startsWith('/') ? fileName : `/${fileName}`\n    const baseUrl = `${playbackBaseUrl}${normalizedFileName}`\n\n    const ts = Math.floor(Date.now() / 1000) + 3600\n    const playbackUrl = this.genTypeAUrl(baseUrl, urlAuthPrimaryKey, ts)\n\n    logger.debug({ playbackUrl, fileName }, '拼接播放 URL')\n\n    // 使用 fetch 流式下载并上传\n    const response = await fetch(playbackUrl)\n    if (!response.ok || !response.body) {\n      throw new Error(`下载失败: ${response.status}`)\n    }\n\n    const contentLength = response.headers.get('content-length')\n    const size = contentLength ? Number.parseInt(contentLength, 10) : 0\n\n    logger.debug({ size: `${(size / 1024 / 1024).toFixed(2)} MB` }, '开始流式上传')\n\n    const arrayBuffer = await response.arrayBuffer()\n    const buffer = Buffer.from(arrayBuffer)\n\n    const result = await assetsService.uploadFromStream(userId, buffer, {\n      type: assetType,\n      mimeType: 'video/mp4',\n      size,\n    }, subPath)\n\n    return assetsService.buildUrl(result.asset.path)\n  }","sourceCodeStart":143,"sourceCodeEnd":179,"githubUrl":"https://github.com/yikart/AiToEarn/blob/d3aa8bea5b146a8675607cf0144d891aad3e9683/project/aitoearn-backend/apps/aitoearn-ai/src/core/agent/mcp/volcengine/volcengine.utils.ts#L143-L179","documentation":"getVideoComments takes videoId as a route param and accountId as a query param; both must be present or BadRequestException('videoId和accountId是必须的') is thrown. limit and cursor are optional pagination parameters.","triggerScenarios":"GET the comments route (/.../:videoId/comments style) without ?accountId=, or a malformed URL where the videoId path segment is empty so routing still matches but the param is empty.","commonSituations":"Client builds the comment-view URL from a notification payload that only contains videoId and not the account, or template-string interpolation with an undefined accountId produces the literal string 'undefined' vs being dropped — validate before calling.","solutions":["Include ?accountId=<id> alongside the videoId path segment.","Guard against JavaScript interpolation producing empty/'undefined' segments in the URL.","Store accountId with any video reference used for comment views."],"exampleFix":"// before\nawait api.get(`/plat/tiktok/video/${videoId}/comments?limit=20`);\n// after\nawait api.get(`/plat/tiktok/video/${videoId}/comments?accountId=${encodeURIComponent(accountId)}&limit=20`);","handlingStrategy":"validation","validationCode":"if (!videoId || !accountId) throw new Error('videoId和accountId是必须的');\nawait api.get(`/plat/tiktok/video/${videoId}/comments`, { params: { accountId, limit, cursor } });","typeGuard":"function canLoadComments(videoId: unknown, accountId: unknown): accountId is string {\n  return typeof videoId === 'string' && videoId.length > 0 && typeof accountId === 'string' && accountId.length > 0;\n}","tryCatchPattern":"try {\n  return await api.get(`/plat/tiktok/video/${videoId}/comments`, { params: { accountId, limit, cursor } });\n} catch (e) {\n  if (e.response?.status === 400) return { comments: [], cursor: null };\n  throw e;\n}","preventionTips":["Include accountId as a query param on every comments request","Sanitize path segments to avoid 'undefined' string interpolation","Store accountId with any video reference used in comment views"],"tags":["validation","missing-parameter","query-params"],"backgroundTag":"missing-required-parameter","analyzedSha":"d3aa8bea5b146a8675607cf0144d891aad3e9683","analyzedAt":"2026-08-31T14:19:24.185Z","schemaVersion":2},"datasetVersion":"2026-08-31T19:17:28.585Z"}