{"record":{"id":"360fe6ba1dbc9ced","repo":"harry0703/MoneyPrinterTurbo","slug":"description-file-must-exist-inside-resource-dir","errorCode":null,"errorMessage":"{description} file must exist inside {resource_dir}: {raw_path}","messagePattern":"(.+?) file must exist inside (.+?): (.+?)","errorType":"exception","errorClass":"ValueError","httpStatus":null,"severity":"warning","filePath":"cli.py","lineNumber":641,"sourceCode":"    \"\"\"解析项目资源文件，并确保绝对路径仍位于对应资源目录内。\"\"\"\n    from app.utils import utils\n\n    expanded_path = os.path.expanduser(raw_path.strip())\n    candidates = (\n        [expanded_path]\n        if os.path.isabs(expanded_path)\n        else [\n            os.path.join(resource_dir, expanded_path),\n            os.path.join(utils.root_dir(), expanded_path),\n        ]\n    )\n    for candidate in candidates:\n        resolved_path = os.path.realpath(candidate)\n        if os.path.isfile(resolved_path) and _path_is_within_directory(\n            resolved_path, resource_dir\n        ):\n            return resolved_path\n    raise ValueError(\n        f\"{description} file must exist inside {resource_dir}: {raw_path}\"\n    )\n\n\ndef prepare_cli_files(params: VideoParams, stop_at: str) -> None:\n    \"\"\"\n    在调用 LLM/TTS 前准备 CLI 文件，避免长流程运行到后期才报告路径错误。\n\n    服务层为了保护 API 请求，只允许读取 ``storage/local_videos`` 内的素材。\n    CLI 是本地入口，接受当前目录相对路径和绝对路径。目录外素材会\n    复制到受控目录，再把参数替换为服务层可安全使用的绝对路径。\n    \"\"\"\n    from app.models import const\n    from app.services import bgm as bgm_service\n    from app.utils import utils\n\n    local_material_extensions = {\n        *(f\".{extension}\" for extension in const.FILE_TYPE_VIDEOS),","sourceCodeStart":623,"sourceCodeEnd":659,"githubUrl":"https://github.com/harry0703/MoneyPrinterTurbo/blob/1f9f19c2021a68d04df228f33e9099a0c947f6f8/cli.py#L623-L659","documentation":"Raised by the stricter CLI resource resolver: it searches the file under the resource directory (and repo root), then accepts it only if the resolved real path both exists as a file and is contained within resource_dir (via the commonpath check, which also rejects symlink escapes). No candidate matched, so the file is not a valid in-resource asset.","triggerScenarios":"Passing a font/subtitle/song asset path that lives outside the resource directory; passing an absolute path to a file elsewhere on disk; a file inside resource_dir that is a symlink to an external location; simple filename typo where the file is not in the resource dir at all.","commonSituations":"User points --font/--bgm at a personal file outside the bundled resource folder; resources were relocated between versions; symlinked resource used to work but now fails the containment check.","solutions":["Place the asset inside the expected resource directory and pass the relative name.","Verify the file exists there: the resolver checks <resource_dir>/<name> then <repo_root>/<name>.","Remove symlinks inside the resource dir that point outside — realpath-based containment rejects them."],"exampleFix":"# before\npython cli.py --font /home/user/fonts/custom.ttf\n\n# after  # e.g. put the font under the resource dir\ncp /home/user/fonts/custom.ttf resource/fonts/\npython cli.py --font custom.ttf","handlingStrategy":"validation","validationCode":"import os\n\ndef resource_file_exists(resource_dir: str, raw: str) -> bool:\n    for base in (resource_dir, utils_root_dir()):\n        cand = os.path.realpath(os.path.join(base, os.path.expanduser(raw)))\n        if os.path.isfile(cand):\n            try:\n                inside = os.path.commonpath([os.path.realpath(resource_dir), cand]) == os.path.realpath(resource_dir)\n            except ValueError:\n                inside = False\n            if inside:\n                return True\n    return False","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Install custom assets into the resource directory instead of referencing external paths.","Avoid symlinking resource files to external locations; copy them in."],"tags":["cli","resources","path-security","file-not-found"],"backgroundTag":null,"analyzedSha":"1f9f19c2021a68d04df228f33e9099a0c947f6f8","analyzedAt":"2026-08-14T19:41:05.568Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}