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.dataView on GitHub (pinned to d3aa8bea5b)
Solutions
- Inspect the stringified result embedded in the error to see what the relay actually returned
- 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)
- Check relay service health/changelog for a response schema change to RelayVideoSubmitResponse
- 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
- Pin and monitor relay service versions for response contract changes
- Verify RELAY_SERVER_URL hosts the video endpoint before rollout
- Alert on any createVideo failure — the embedded stringified body tells you what the relay returned
- Ensure submit parameters pass relay validation so a task is actually enqueued
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
- InvalidModel
- AiCallFailed
- body.code
- Relay API error [${body.code}]: ${body.message}
- Relay uploadSign returned no uploadUrl: ${JSON.stringify(sig
AI-assisted analysis of yikart/AiToEarn@d3aa8bea5b (2026-08-31).
Data as JSON: /api/errors/5ce76baeab5b9d1e.
Report an issue: GitHub.