{"record":{"id":"36d25dcfb82af66d","repo":"affaan-m/ECC","slug":"asset-must-contain-a-video-stream-path","errorCode":null,"errorMessage":"asset must contain a video stream: {path}","messagePattern":"asset must contain a video stream: (.+?)","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"skills/taste-application/scripts/tasteforge/resolve.py","lineNumber":62,"sourceCode":"            \"-v\",\n            \"error\",\n            \"-select_streams\",\n            \"v:0\",\n            \"-count_frames\",\n            \"-show_entries\",\n            \"stream=avg_frame_rate,r_frame_rate,nb_read_frames,pix_fmt\",\n            \"-of\",\n            \"json\",\n            str(path),\n        ],\n        capture_output=True,\n        text=True,\n        check=True,\n        timeout=120,\n    )\n    streams = json.loads(result.stdout).get(\"streams\", [])\n    if len(streams) != 1:\n        raise ValueError(f\"asset must contain a video stream: {path}\")\n    stream = streams[0]\n    average = _fps(stream[\"avg_frame_rate\"])\n    if average != _fps(stream[\"r_frame_rate\"]):\n        raise ValueError(f\"variable or ambiguous frame rate: {path}\")\n    pixel_format = stream.get(\"pix_fmt\", \"\")\n    alpha = pixel_format.startswith((\"yuva\", \"gbrap\")) or pixel_format in {\n        \"rgba\",\n        \"bgra\",\n        \"argb\",\n        \"abgr\",\n        \"rgba64be\",\n        \"rgba64le\",\n        \"bgra64be\",\n        \"bgra64le\",\n        \"ya8\",\n        \"ya16be\",\n        \"ya16le\",\n    }","sourceCodeStart":44,"sourceCodeEnd":80,"githubUrl":"https://github.com/affaan-m/ECC/blob/8321021c54d670126ce3b2969d5deb880b4b0c2a/skills/taste-application/scripts/tasteforge/resolve.py#L44-L80","documentation":"probe_asset runs ffprobe on an asset and requires exactly one video stream; it raises ValueError('asset must contain a video stream: {path}') when ffprobe returns zero streams or more than one. Zero usually means the file has no video track; multiple streams means the probe filter was ambiguous and the tool refuses to guess.","triggerScenarios":"Probing an audio-only file (mp3, wav), a subtitle/srt file, a corrupted download, or an asset where ffprobe's stream selection yields several video streams (e.g. multiple video tracks or unusual container layouts).","commonSituations":"Wrong file passed as overlay asset (audio narration instead of animation); a .webm with attached cover art adding a second (mjpeg) stream; truncated or zero-byte file that ffprobe can't parse.","solutions":["Verify the file is actually a video: run `ffprobe -v error -show_streams <path>` yourself and check the stream list","If the asset is audio-only, re-export or replace it with a video overlay (e.g. an alpha-capable .webm/.mov)","If there are two video streams (e.g. cover-art + video), strip the extra stream: `ffmpeg -i in.mkv -map 0:v:0 -c copy out.mkv`","If the file is corrupted, re-download/re-encode it and confirm one video stream before retrying"],"exampleFix":"# before\ninfo = resolve.probe_asset('narration.mp3')  # audio only\n# after\ninfo = resolve.probe_asset('overlay_animation.webm')  # single video stream","handlingStrategy":"validation","validationCode":"import json, subprocess\n\ndef has_single_video_stream(path):\n    r = subprocess.run(['ffprobe', '-v', 'error', '-show_streams', '-of', 'json', str(path)],\n                       capture_output=True, text=True)\n    streams = json.loads(r.stdout).get('streams', [])\n    return len(streams) == 1\n\nassert has_single_video_stream('overlay.webm')","typeGuard":null,"tryCatchPattern":"try:\n    info = resolve.probe_asset(path)\nexcept ValueError as e:\n    if 'must contain a video stream' in str(e):\n        raise AssetError(f'{path} is not a single-stream video') from e\n    raise","preventionTips":["Verify asset type before feeding it to the pipeline (extension + ffprobe)","Map/strip extra streams (cover art) when muxing assets","Validate downloads (size, ffprobe success) before registering them as assets"],"tags":["video","ffprobe","asset"],"backgroundTag":"unexpected-response-shape","analyzedSha":"8321021c54d670126ce3b2969d5deb880b4b0c2a","analyzedAt":"2026-09-16T10:08:13.343Z","contentChangedAt":"2026-09-16T10:08:13.343Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}