harry0703/MoneyPrinterTurbo · error · ValueError
unsupported local material type {extension or '<none>'}: {ma
Error message
unsupported local material type {extension or '<none>'}: {material.url}; allowed extensions: {allowed} What it means
Thrown while validating --video-materials for local video source tasks. Each material URL is first resolved via _resolve_cli_file (against the local_videos storage dir), then its lowercase extension is checked against local_material_extensions, which is const.FILE_TYPE_VIDEOS + const.FILE_TYPE_IMAGES plus .avi and .flv (cli.py:658-663). Any extension outside that set — including no extension at all ('<none>') — aborts the run. Validation happens for all materials before any copy, so no orphan files are left behind.
Source
Thrown at cli.py:730
raise ValueError("subtitle font must use the .ttf or .ttc extension")
# 下游根据 resource/fonts 内的文件名拼接路径,因此仍保留纯文件名。
params.font_name = os.path.basename(font_path)
if params.video_source != "local" or stop_at not in {"materials", "video"}:
return
local_videos_dir = utils.storage_dir("local_videos", create=True)
resolved_materials: list[tuple[MaterialInfo, str, str]] = []
for material in params.video_materials or []:
source_path = _resolve_cli_file(
material.url,
description="local material",
fallback_dir=local_videos_dir,
)
extension = os.path.splitext(source_path)[1].lower()
if extension not in local_material_extensions:
allowed = ", ".join(sorted(local_material_extensions))
raise ValueError(
f"unsupported local material type {extension or '<none>'}: "
f"{material.url}; allowed extensions: {allowed}"
)
resolved_materials.append((material, source_path, extension))
# 所有输入检查通过后再复制,避免第二个文件无效时留下第一个文件的
# 孤儿副本。
prepared_paths: dict[str, str] = {}
for material, source_path, extension in resolved_materials:
prepared_path = prepared_paths.get(source_path)
if prepared_path is None:
if _path_is_within_directory(source_path, local_videos_dir):
prepared_path = source_path
else:
prepared_path = os.path.join(
local_videos_dir,
f"cli-material-{uuid4().hex}{extension}",
)View on GitHub (pinned to 1f9f19c202)
Solutions
- Convert the offending material to a supported format (mp4/webm/jpg/png etc., or avi/flv) with ffmpeg before passing it
- Fix the filename so it has the correct supported extension — check the exact allowed list printed in the error message
- Add the extension to const.FILE_TYPE_VIDEOS or const.FILE_TYPE_IMAGES in app/constants if the format is genuinely supported downstream, then rerun (cli.py builds the allowed set from those constants)
Example fix
# before --video-source local --stop-at video \ --video-material clip.mkv # after ffmpeg -i clip.mkv -c:v libx264 -c:a aac clip.mp4 --video-source local --stop-at video \ --video-material clip.mp4
Defensive patterns
Strategy: validation
Validate before calling
import os
from app import const
def build_local_material_extensions() -> set[str]:
return {
*(f".{e}" for e in const.FILE_TYPE_VIDEOS),
*(f".{e}" for e in const.FILE_TYPE_IMAGES),
".avi",
".flv",
}
def materials_are_valid(paths: list[str]) -> bool:
allowed = build_local_material_extensions()
return all(os.path.splitext(p)[1].lower() in allowed for p in paths) Type guard
def is_supported_material(path: str) -> bool:
"""Guard: material path has an extension in the local-material allowlist."""
return isinstance(path, str) and os.path.splitext(path)[1].lower() in build_local_material_extensions() Prevention
- Derive the allowed extension set from const.FILE_TYPE_VIDEOS/IMAGES instead of maintaining a second list
- Validate every material path before submitting the job — the CLI validates all-before-copy, so should the caller
- Run a preflight loop printing each material with its resolved extension to catch '<none>' cases
When it happens
Trigger: CLI run with --video-source local, stop_at in {materials, video}, and a --video-material entry whose resolved path ends in an unsupported extension (e.g. .gifv, .webp if not in const lists, .mkv if excluded, .txt) or has no extension; also triggered by directories or misresolved relative paths.
Common situations: Mixing in an unsupported format like .mkv/.wmv/.bmp that const.FILE_TYPE_* does not include; a material filename missing its extension after download; passing a URL-style string that _resolve_cli_file resolves to something unexpected; Windows-hidden double extensions.
Related errors
- {request_id}: Only files with extensions {', '.join(allowed_
- {description} path cannot be empty
- unsupported custom audio type {audio_extension or '<none>'};
- --bgm-file is required when --bgm-type is custom
- background music must be a supported audio file inside stora
AI-assisted analysis of harry0703/MoneyPrinterTurbo@1f9f19c202 (2026-08-14).
Data as JSON: /api/errors/8a635efcf4a62623.
Report an issue: GitHub.