yikart/AiToEarn · warning · Error

下载失败: ${response.status}

Error message

下载失败: ${response.status}

What it means

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.

Source

Thrown at project/aitoearn-backend/apps/aitoearn-ai/src/core/agent/mcp/volcengine/volcengine.utils.ts:161

    const urlAuthPrimaryKey = config.ai.volcengine?.urlAuthPrimaryKey

    if (!playbackBaseUrl || !urlAuthPrimaryKey) {
      logger.warn({ fileName }, 'volcengine 未配置,跳过播放 URL 拼接')
      return undefined
    }

    const normalizedFileName = fileName.startsWith('/') ? fileName : `/${fileName}`
    const baseUrl = `${playbackBaseUrl}${normalizedFileName}`

    const ts = Math.floor(Date.now() / 1000) + 3600
    const playbackUrl = this.genTypeAUrl(baseUrl, urlAuthPrimaryKey, ts)

    logger.debug({ playbackUrl, fileName }, '拼接播放 URL')

    // 使用 fetch 流式下载并上传
    const response = await fetch(playbackUrl)
    if (!response.ok || !response.body) {
      throw new Error(`下载失败: ${response.status}`)
    }

    const contentLength = response.headers.get('content-length')
    const size = contentLength ? Number.parseInt(contentLength, 10) : 0

    logger.debug({ size: `${(size / 1024 / 1024).toFixed(2)} MB` }, '开始流式上传')

    const arrayBuffer = await response.arrayBuffer()
    const buffer = Buffer.from(arrayBuffer)

    const result = await assetsService.uploadFromStream(userId, buffer, {
      type: assetType,
      mimeType: 'video/mp4',
      size,
    }, subPath)

    return assetsService.buildUrl(result.asset.path)
  }

View on GitHub (pinned to d3aa8bea5b)

Solutions

  1. Include ?accountId=<id> alongside the videoId path segment.
  2. Guard against JavaScript interpolation producing empty/'undefined' segments in the URL.
  3. Store accountId with any video reference used for comment views.

Example fix

// before
await api.get(`/plat/tiktok/video/${videoId}/comments?limit=20`);
// after
await api.get(`/plat/tiktok/video/${videoId}/comments?accountId=${encodeURIComponent(accountId)}&limit=20`);
Defensive patterns

Strategy: validation

Validate before calling

if (!videoId || !accountId) throw new Error('videoId和accountId是必须的');
await api.get(`/plat/tiktok/video/${videoId}/comments`, { params: { accountId, limit, cursor } });

Type guard

function canLoadComments(videoId: unknown, accountId: unknown): accountId is string {
  return typeof videoId === 'string' && videoId.length > 0 && typeof accountId === 'string' && accountId.length > 0;
}

Try / catch

try {
  return await api.get(`/plat/tiktok/video/${videoId}/comments`, { params: { accountId, limit, cursor } });
} catch (e) {
  if (e.response?.status === 400) return { comments: [], cursor: null };
  throw e;
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Understand the failure class

Background: Missing required parameter errors: what 'X is required' and 'the required X param is missing' mean, and how to fix them — this error's family across 27 libraries.

Related errors


AI-assisted analysis of yikart/AiToEarn@d3aa8bea5b (2026-08-31). Data as JSON: /api/errors/9ca6a0e772158ca7. Report an issue: GitHub.