{"record":{"id":"297c1483374f52d8","repo":"ATH-MaaS/Pixelle-Video","slug":"resource-not-found-os-path-join-resource-type","errorCode":null,"errorMessage":"Resource not found: {os.path.join(resource_type, *paths)}\n  Searched locations:\n    1. {custom_path} (custom)\n    2. {default_path} (default)","messagePattern":"Resource not found: (.+?)\n  Searched locations:\n    1\\. (.+?) \\(custom\\)\n    2\\. (.+?) \\(default\\)","errorType":"exception","errorClass":"FileNotFoundError","httpStatus":null,"severity":"error","filePath":"pixelle_video/utils/os_util.py","lineNumber":372,"sourceCode":"        \n        >>> get_resource_path(\"workflows\", \"selfhost\", \"image_flux.json\")\n        # Returns: \"data/workflows/selfhost/image_flux.json\" or \"workflows/selfhost/image_flux.json\"\n    \"\"\"\n    # Build custom path (data/*)\n    custom_path = get_data_path(resource_type, *paths)\n    \n    # Build default path (root/*)\n    default_path = get_root_path(resource_type, *paths)\n    \n    # Priority: custom > default\n    if os.path.exists(custom_path):\n        return custom_path\n    \n    if os.path.exists(default_path):\n        return default_path\n    \n    # Not found in either location\n    raise FileNotFoundError(\n        f\"Resource not found: {os.path.join(resource_type, *paths)}\\n\"\n        f\"  Searched locations:\\n\"\n        f\"    1. {custom_path} (custom)\\n\"\n        f\"    2. {default_path} (default)\"\n    )\n\n\ndef list_resource_files(\n    resource_type: Literal[\"bgm\", \"templates\", \"workflows\"],\n    subdir: str = \"\"\n) -> list[str]:\n    \"\"\"\n    List resource files with custom override support\n    \n    Merges files from both default and custom locations:\n        - Files from data/{resource_type}/* (custom, higher priority)\n        - Files from {resource_type}/* (default)\n        - Duplicate names are deduplicated (custom takes precedence)","sourceCodeStart":354,"sourceCodeEnd":390,"githubUrl":"https://github.com/ATH-MaaS/Pixelle-Video/blob/848b054e4fae40dabc62ec58e960b573e83793ac/pixelle_video/utils/os_util.py#L354-L390","documentation":"get_resource_path resolves bundled resources (templates, workflows, BGM, etc.) by checking a custom overrides directory first, then the default packaged location. If the joined resource path exists in neither, it raises FileNotFoundError listing both searched locations. It means the requested resource_type/paths combination is not shipped with the package and no custom override was provided.","triggerScenarios":"Calls like get_resource_path(\"templates\", \"1080x1920\", \"default.html\"), _scan_workflows, _resolve_bgm_path, resolve_template_path, or list_local_media_workflows referencing a size/name that does not exist on disk — e.g. a typo'd template name, a resolution the package does not include, or a missing custom resource directory.","commonSituations":"Requesting a resolution (e.g. 2048x2048) the library does not bundle; resources directory stripped by packaging (wheel/sdist excludes data files or PyInstaller missing data hooks); project moved to a machine without the custom assets folder; renaming template files without updating config.","solutions":["Read the two searched paths from the error and check which one you were expected to provide — create the file in the custom location.","Verify the exact resource name/spelling and available sizes via list_templates_for_size or by listing the package's templates directory.","If packaging stripped data files, reinstall the package or include resource dirs in your build (package_data / PyInstaller --add-data).","Point the custom resource directory configuration at a folder containing the needed resource.","Catch FileNotFoundError and fall back to a supported default template/size."],"exampleFix":"// before\ntpl = get_template_full_path(\"4096x2160\", \"cinematic.html\")\n// after\ntry:\n    tpl = get_template_full_path(\"4096x2160\", \"cinematic.html\")\nexcept FileNotFoundError:\n    tpl = get_template_full_path(\"1080x1920\", \"default.html\")  # bundled fallback","handlingStrategy":"fallback","validationCode":"import os\nfrom pixelle_video.utils.os_util import get_resource_path  # or reimplement the two-location probe\n\ndef resource_exists(resource_type: str, *paths: str) -> bool:\n    try:\n        get_resource_path(resource_type, *paths)\n        return True\n    except FileNotFoundError:\n        return False\n# check before rendering: resource_exists(\"templates\", \"1080x1920\", \"default.html\")","typeGuard":"def resolve_or_none(resource_type: str, *paths: str):\n    try:\n        return get_resource_path(resource_type, *paths)\n    except FileNotFoundError:\n        return None","tryCatchPattern":"try:\n    tpl = get_template_full_path(size, name)\nexcept FileNotFoundError as e:\n    logger.error(\"Resource missing: %s\", e)\n    tpl = get_template_full_path(\"1080x1920\", \"default.html\")  # bundled fallback","preventionTips":["List available resources (list_templates_for_size, _scan_workflows) before referencing one","Keep custom resource directories configured and present in every environment","Ensure build/packaging includes data files (package_data, PyInstaller --add-data)","Validate configured template/BGM/workflow names at startup"],"tags":["file-not-found","resources","configuration","packaging"],"backgroundTag":"resource-file-not-found","analyzedSha":"848b054e4fae40dabc62ec58e960b573e83793ac","analyzedAt":"2026-08-30T03:24:41.468Z","schemaVersion":2},"datasetVersion":"2026-08-30T08:17:16.595Z"}