yikart/AiToEarn · error · AppException

AiCallFailed

AiCallFailed

Error message

AiCallFailed

What it means

volcengine.service.ts 的通用 checkApiResponseError 会检查所有 VOD API 响应:当 ResponseMetadata.Error 存在时,把火山原始错误 Code/Message 包装为 AiCallFailed 抛出。这是所有火山 OpenAPI 业务级错误在库内的统一出口。

Source

Thrown at project/aitoearn-backend/apps/aitoearn-ai/src/core/ai/libs/volcengine/volcengine.service.ts:352

      serviceName: 'vod',
    })
  }

  /**
   * 检查 API 响应中的错误并抛出异常(内部方法)
   */
  private checkApiResponseError<T>(
    response: OpenApiResponse<T>,
    operation: string,
    requestData?: unknown,
  ): asserts response is OpenApiResponse<T> & { Result: T } {
    if (response.ResponseMetadata?.Error) {
      const error = response.ResponseMetadata.Error
      this.logger.error(
        { error, requestData },
        `${operation} failed: ${error.Code || 'Unknown'} - ${error.Message}`,
      )
      throw new AppException(ResponseCode.AiCallFailed, {
        code: error.Code || 'Unknown',
        message: error.Message,
      })
    }

    if (!response.Result) {
      this.logger.error({ requestData }, `${operation} returned no result`)
      throw new AppException(ResponseCode.AiCallFailed, {
        message: typeof response === 'string' ? response : 'No result returned',
      })
    }
  }
}

View on GitHub (pinned to d3aa8bea5b)

Solutions

  1. 解析异常中的 code/message(原始火山错误码),按码定位:SignatureDoesNotMatch→检查密钥、InvalidParameter→修参数、RequestLimitExceeded→退避重试。
  2. 核对 VolcengineConfig 注入的 accessKeyId/secretAccessKey/spaceName 是否为当前有效值。
  3. 确认对应火山服务已在控制台开通且地域正确。
  4. 对可重试错误码实现指数退避重试。

Example fix

// before
const res = await volcengineService.getUploadTaskInfo(req)
// after
try {
  const res = await volcengineService.getUploadTaskInfo(req)
} catch (e) {
  const { code, message } = (e as any).data ?? {}
  logger.error({ code, message }, 'Volcengine API failed')
  if (RETRYABLE_CODES.includes(code)) return retryWithBackoff(() => volcengineService.getUploadTaskInfo(req))
  throw e
}
Defensive patterns

Strategy: try-catch

Validate before calling

for (const k of ['VOLC_ACCESS_KEY_ID', 'VOLC_SECRET_ACCESS_KEY', 'VOLCSPACE_NAME']) {
  if (!process.env[k]) throw new Error(`Missing Volcengine env var: ${k}`)
}

Type guard

function isVolcengineApiError(e: unknown): e is AppException & { data: { code: string, message: string } } {
  return e instanceof AppException
    && (e as any).code === ResponseCode.AiCallFailed
    && typeof (e as any).data?.code === 'string'
}

Try / catch

try {
  return await volcengineService.someOperation(req)
} catch (e) {
  if (isVolcengineApiError(e)) {
    const { code, message } = e.data
    if (RETRYABLE.includes(code)) return retryWithBackoff(() => volcengineService.someOperation(req))
    throw new Error(`Volcengine ${code}: ${message}`)
  }
  throw e
}

Prevention

When it happens

Trigger: 任何经 volcengineService(submitDirectEditTaskAsync、getDirectEditResult、uploadMediaByUrl、uploadMaterial、getUploadTaskInfo 等)的调用返回带 ResponseMetadata.Error 的响应:签名错误、权限不足、参数非法、限流、空间不存在、内部错误等。

Common situations: AK/SK 配错或轮换后未更新;未开通 VOD/智能剪辑服务;SpaceName 不存在;请求参数不符合 API 版本 2018-01-01 规范;火山侧限流或故障。

Related errors


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