yikart/AiToEarn · error · BadRequestException

DashScope HappyHorse does not support image_tail

Error message

DashScope HappyHorse does not support image_tail

What it means

buildPayload for DashScope HappyHorse models rejects requests containing image_tail, since the DashScope HappyHorse API has no tail-image capability. It throws BadRequestException immediately before building the payload.

Source

Thrown at project/aitoearn-backend/apps/aitoearn-ai/src/core/ai/video/dashscope/dashscope.service.ts:153

    const metadata = await this.videoMetadataService.probeVideoMetadata(FileUtil.buildUrl(videoUrl))
    const metadataDuration = Number(metadata.duration)
    if (!Number.isFinite(metadataDuration) || metadataDuration <= 0) {
      throw new BadRequestException('video duration is required')
    }

    return Math.min(Math.ceil(metadataDuration), this.getMaxDuration(modelConfig))
  }

  private async buildPayload(request: UserVideoGenerationRequestDto, modelConfig: DashscopeModelConfig): Promise<{
    providerModel: string
    mode: string
    payload: DashscopeCreateVideoTaskRequest
    duration?: number
    resolution?: string
  }> {
    if (request.image_tail) {
      throw new BadRequestException('DashScope HappyHorse does not support image_tail')
    }

    const imageUrls = this.collectImageUrls(request)
    const videoUrl = request.video_url ?? request.videos?.[0]
    const mode = this.resolveMode(request, modelConfig, imageUrls)
    const resolution = request.resolution
    const providerModel = this.getProviderModel(modelConfig, mode, resolution)
    const ratio = request.ratio
    const duration = await this.resolveVideoDuration(request, modelConfig, mode === 'video2video' ? videoUrl : undefined)
    const prompt = request.prompt
    const parameters: NonNullable<DashscopeCreateVideoTaskRequest['parameters']> = {}
    if (resolution) {
      parameters.resolution = resolution
    }
    parameters.watermark = request.watermark ?? false
    if (request.seed != null) {
      parameters.seed = request.seed
    }

View on GitHub (pinned to d3aa8bea5b)

Solutions

  1. Omit image_tail for DashScope HappyHorse requests; use only image_head.
  2. Route requests needing image_tail to a provider/model that supports it.
  3. Strip image_tail in an adapter layer before calling DashScope generation.
  4. If image_tail was intended as a second reference image, move it into the images array (respecting image count limits).

Example fix

// before
{ model: 'wan-happyhorse', image_head: urlA, image_tail: urlB }
// after
{ model: 'wan-happyhorse', image_head: urlA } // image_tail unsupported on DashScope
Defensive patterns

Strategy: validation

Validate before calling

if (model.startsWith('wan-happyhorse') && request.image_tail) {
  throw new Error('Strip image_tail before calling DashScope HappyHorse; it is unsupported')
}

Type guard

function happyhorseSafe<T extends { image_tail?: string }>(req: T, model: string): Omit<T, 'image_tail'> {
  return model.includes('happyhorse') ? omit(req, 'image_tail') : req
}

Try / catch

try {
  await generate(request)
} catch (err) {
  if (err.message.includes('does not support image_tail')) {
    const { image_tail, ...rest } = request
    return generate(rest as typeof request) // retry without the unsupported field
  }
  throw err
}

Prevention

When it happens

Trigger: Any video generation request to a HappyHorse (DashScope) model with request.image_tail set (e.g. clients that support start/end frames for other providers reusing the same DTO).

Common situations: Frontend sends image_tail unconditionally when a user supplies a second frame image; code shared across providers (e.g. one that supports image_tail like Kling) targeting DashScope.

Related errors


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