{"record":{"id":"198da34ab47b8735","repo":"yikart/AiToEarn","slug":"image-tail-requires-a-single-first-frame-image","errorCode":null,"errorMessage":"image_tail requires a single first frame image","messagePattern":"image_tail requires a single first frame image","errorType":"validation","errorClass":"BadRequestException","httpStatus":400,"severity":"warning","filePath":"project/aitoearn-backend/apps/aitoearn-ai/src/core/ai/video/volcengine/volcengine.service.ts","lineNumber":72,"sourceCode":"    const referenceAudios = request.audios || []\n    const hasExplicitReferenceMedia = additionalReferenceImages.length > 0 || referenceVideos.length > 0 || referenceAudios.length > 0\n    const usesLegacyFrameInput = !!legacyFrameImage && (!hasExplicitReferenceMedia || !!request.image_tail)\n    const referenceImages = [\n      ...additionalReferenceImages,\n      ...(!usesLegacyFrameInput && legacyFrameImage ? [legacyFrameImage] : []),\n    ]\n\n    if (usesLegacyFrameInput) {\n      content.push({\n        type: ContentType.ImageUrl,\n        image_url: { url: await this.toAccessibleUrl(legacyFrameImage) || legacyFrameImage },\n        role: ImageRole.FirstFrame,\n      })\n    }\n\n    if (request.image_tail) {\n      if (!legacyFrameImage) {\n        throw new BadRequestException('image_tail requires a single first frame image')\n      }\n      content.push({\n        type: ContentType.ImageUrl,\n        image_url: { url: await this.toAccessibleUrl(request.image_tail) || request.image_tail },\n        role: ImageRole.LastFrame,\n      })\n    }\n\n    if (!request.image_tail) {\n      for (const imageUrl of referenceImages) {\n        content.push({\n          type: ContentType.ImageUrl,\n          image_url: { url: await this.toAccessibleUrl(imageUrl) || imageUrl },\n          role: usesLegacyFrameInput && imageUrl === legacyFrameImage ? ImageRole.FirstFrame : ImageRole.ReferenceImage,\n        })\n      }\n    }\n","sourceCodeStart":54,"sourceCodeEnd":90,"githubUrl":"https://github.com/yikart/AiToEarn/blob/d3aa8bea5b146a8675607cf0144d891aad3e9683/project/aitoearn-backend/apps/aitoearn-ai/src/core/ai/video/volcengine/volcengine.service.ts#L54-L90","documentation":"BadRequestException (HTTP 400) from the Volcengine video adapter: `image_tail` (last-frame image) is only supported together with a single, scalar first-frame image (`request.image` as a single value, not an array). The legacy first-frame slot is captured in `legacyFrameImage`; if `image` is an array or absent, there is no FirstFrame to pair the LastFrame with and the request is rejected.","triggerScenarios":"Calling createFromRequest (via userVideoGeneration with a Volcengine-channel model) with `image_tail` set while `image` is either undefined or an array of reference images — e.g. `image: [url1, url2], image_tail: tailUrl`, or only `images: [...]` plus `image_tail`.","commonSituations":"Frontend refactored from single-image input to multi-image array input but kept the tail-frame toggle; clients using the new `images` reference-image field and assuming first/last frame still works; migrating from a model that supported tail frames with reference images to one that requires the strict first+last pair.","solutions":["Pass the first frame as the scalar `image` field (not inside an array) whenever you set `image_tail`.","Drop `image_tail` if you are using array/reference-image mode, since the combination is unsupported.","Update the client UI to disable the tail-frame option when multiple reference images are selected.","If the target model genuinely supports first+last with references, extend the adapter to map frames explicitly instead of relying on legacy fields."],"exampleFix":"// before\n{ prompt, image: [firstUrl, refUrl], image_tail: tailUrl } // 400\n// after\n{ prompt, image: firstUrl, image_tail: tailUrl }             // ok\n// or, multi-reference mode:\n{ prompt, image: [firstUrl, refUrl] }                        // no image_tail","handlingStrategy":"validation","validationCode":"if (req.image_tail) {\n  const singleFirstFrame = typeof req.image === 'string'\n  if (!singleFirstFrame) {\n    throw new Error('image_tail requires `image` to be a single URL string, not an array')\n  }\n}","typeGuard":"function canUseTailFrame(req: { image?: string | string[], images?: string[], image_tail?: string }): boolean {\n  return !!req.image_tail && typeof req.image === 'string'\n}","tryCatchPattern":"try {\n  await videoService.userVideoGeneration(req)\n} catch (e) {\n  if (e instanceof BadRequestException && /image_tail/.test(e.message)) {\n    // retry without image_tail, or move first frame to scalar `image`\n  }\n}","preventionTips":["Disable the tail-frame option in the UI whenever multiple images are selected.","Keep first/last frame in scalar `image`/`image_tail`; use `images` only for reference images.","Add a client-side Zod refinement enforcing image_tail ⇒ string image.","Read the target model's frame-input docs before enabling tail frames."],"tags":["validation","bad-request","volcengine","video-generation"],"backgroundTag":"incompatible-image-params","analyzedSha":"d3aa8bea5b146a8675607cf0144d891aad3e9683","analyzedAt":"2026-08-31T14:19:24.185Z","schemaVersion":2},"datasetVersion":"2026-08-31T19:17:28.585Z"}