{"record":{"id":"050c8713237697cb","repo":"yikart/AiToEarn","slug":"video-duration-is-required","errorCode":null,"errorMessage":"video duration is required","messagePattern":"video duration is required","errorType":"validation","errorClass":"BadRequestException","httpStatus":400,"severity":"error","filePath":"project/aitoearn-backend/apps/aitoearn-ai/src/core/ai/video/dashscope/dashscope.service.ts","lineNumber":139,"sourceCode":"    if (!Number.isFinite(maxDuration)) {\n      throw new AppException(ResponseCode.InvalidModel)\n    }\n    return maxDuration\n  }\n\n  private async resolveVideoDuration(request: UserVideoGenerationRequestDto, modelConfig: DashscopeModelConfig, videoUrl: string | undefined): Promise<number | undefined> {\n    if (request.duration != null) {\n      return request.duration\n    }\n\n    if (!videoUrl) {\n      return modelConfig.defaults.duration\n    }\n\n    const metadata = await this.videoMetadataService.probeVideoMetadata(FileUtil.buildUrl(videoUrl))\n    const metadataDuration = Number(metadata.duration)\n    if (!Number.isFinite(metadataDuration) || metadataDuration <= 0) {\n      throw new BadRequestException('video duration is required')\n    }\n\n    return Math.min(Math.ceil(metadataDuration), this.getMaxDuration(modelConfig))\n  }\n\n  private async buildPayload(request: UserVideoGenerationRequestDto, modelConfig: DashscopeModelConfig): Promise<{\n    providerModel: string\n    mode: string\n    payload: DashscopeCreateVideoTaskRequest\n    duration?: number\n    resolution?: string\n  }> {\n    if (request.image_tail) {\n      throw new BadRequestException('DashScope HappyHorse does not support image_tail')\n    }\n\n    const imageUrls = this.collectImageUrls(request)\n    const videoUrl = request.video_url ?? request.videos?.[0]","sourceCodeStart":121,"sourceCodeEnd":157,"githubUrl":"https://github.com/yikart/AiToEarn/blob/d3aa8bea5b146a8675607cf0144d891aad3e9683/project/aitoearn-backend/apps/aitoearn-ai/src/core/ai/video/dashscope/dashscope.service.ts#L121-L157","documentation":"resolveVideoDuration must know the source video's duration for video2video tasks. It probes the video's metadata; if the probe fails or returns a non-positive/non-numeric duration and no explicit duration or default exists, it throws BadRequestException('video duration is required').","triggerScenarios":"video2video request without request.duration and without modelConfig.defaults.duration, where probing videoUrl yields no usable duration (corrupt/unreadable file, unsupported codec, unreachable URL, or a 0-second/empty video).","commonSituations":"Uploaded video has no duration metadata (e.g. raw stream or live recording); storage URL not accessible from the backend so the probe returns nothing; video file truncated on upload.","solutions":["Pass an explicit duration in the request to skip metadata probing.","Verify the video URL is accessible from the backend and the file plays correctly.","Re-encode the video (e.g. with ffmpeg) so it carries proper duration metadata.","Set modelConfig.defaults.duration so a default is used when probing fails."],"exampleFix":"// before\nPOST /video/generate { model: '...', video_url: 'https://.../clip.mov' }\n// after\nPOST /video/generate { model: '...', video_url: 'https://.../clip.mp4', duration: 5 }","handlingStrategy":"validation","validationCode":"if (request.video_url && request.duration == null) {\n  const meta = await probeVideoMetadata(request.video_url)\n  if (!Number.isFinite(Number(meta.duration)) || Number(meta.duration) <= 0) {\n    throw new Error('Source video has no readable duration; pass request.duration explicitly')\n  }\n}","typeGuard":"null","tryCatchPattern":"try {\n  await generate(request)\n} catch (err) {\n  if (err instanceof BadRequestException && err.message === 'video duration is required') {\n    // re-probe the video, re-encode it, or ask the user to supply a duration\n  }\n  throw err\n}","preventionTips":["Always send an explicit duration for video2video requests when known.","Pre-encode uploads with ffmpeg so duration metadata is present.","Verify the storage URL is reachable from the backend before submitting.","Configure modelConfig.defaults.duration as a safe fallback."],"tags":["video-metadata","validation","bad-request"],"backgroundTag":"video-metadata-probe-failed","analyzedSha":"d3aa8bea5b146a8675607cf0144d891aad3e9683","analyzedAt":"2026-08-31T14:19:24.185Z","schemaVersion":2},"datasetVersion":"2026-08-31T19:17:28.585Z"}