yikart/AiToEarn · error · AppException

AiCallFailed

AiCallFailed

Error message

Relay video task id is missing: ${this.stringifyForError(result)}

What it means

createVideo posts to the relay endpoint /api/ai/video/generations and requires the response to include a task id (result.id) for later polling. If the relay accepted the request but the body lacks an id (or the body is empty/unexpected shape), it throws AppException(AiCallFailed) including the stringified response body for debugging.

Source

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

      return String(value)
    }
  }

  /**
   * 提交视频生成任务到上游 relay 服务端
   * 对应上游 POST /ai/video/generations
   */
  async createVideo(request: RelayVideoGenerationRequest): Promise<RelayVideoSubmitResponse> {
    return this.aiAvailability.execute(
      { provider: 'relay', operation: 'createVideo', model: request.model },
      async () => {
        const response: AxiosResponse<RelayVideoSubmitResponse> = await this.httpClient.post(
          '/api/ai/video/generations',
          request,
        )
        const result = response.data
        if (!result?.id) {
          throw new AppException(ResponseCode.AiCallFailed, { error: `Relay video task id is missing: ${this.stringifyForError(result)}` })
        }
        return result
      },
    )
  }

  /**
   * 轮询上游 relay 服务端的视频任务状态
   * 对应上游 GET /ai/video/generations/:taskId
   */
  async getVideo(taskId: string): Promise<RelayVideoCallbackDto> {
    return this.aiAvailability.execute(
      { provider: 'relay', operation: 'getVideo' },
      async () => {
        const response: AxiosResponse<RelayVideoCallbackDto> = await this.httpClient.get(
          `/api/ai/video/generations/${encodeURIComponent(taskId)}`,
        )
        return response.data

View on GitHub (pinned to d3aa8bea5b)

Solutions

  1. Inspect the stringified result embedded in the error to see what the relay actually returned
  2. Verify RELAY_SERVER_URL points to the correct environment and the video endpoint exists there (cn: https://aitoearn.cn/api, intl: https://aitoearn.ai/api)
  3. Check relay service health/changelog for a response schema change to RelayVideoSubmitResponse
  4. Confirm the submitted request passes relay-side validation (model, duration, etc.) so it truly enqueues a task

Example fix

// before
const result = await relayService.createVideo(request) // throws AiCallFailed if id missing
// after
try {
  const result = await relayService.createVideo(request)
} catch (e) {
  logger.error({ detail: e }, 'relay createVideo returned no task id')
  throw new ServiceUnavailableException('Video task could not be created at relay; see server logs')
}
Defensive patterns

Strategy: try-catch

Try / catch

try {
  const task = await relayService.createVideo(req)
} catch (e) {
  if (e instanceof AppException && e.code === ResponseCode.AiCallFailed && String(e.message).includes('task id is missing')) {
    logger.error({ detail: e.message }, 'relay video submit returned no id')
    throw new ServiceUnavailableException('Video task submission failed at relay')
  }
  throw e
}

Prevention

When it happens

Trigger: Relay returns 200 with an unexpected body (error payload not caught earlier, HTML error page from a proxy, changed response schema), a gateway/timeouts middleware rewrites the response, or request params are silently ignored causing the relay to return an ack without a task.

Common situations: Relay service version mismatch with client expectations; proxy/load balancer intercepting the POST; API contract change on /api/ai/video/generations; environment misconfiguration routing to the wrong relay host.

Related errors


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