{"record":{"id":"b57bd12099506d96","repo":"invoke-ai/InvokeAI","slug":"video-urls-not-found","errorCode":null,"errorMessage":"Video URLs not found","messagePattern":"Video URLs not found","errorType":"http","errorClass":"HTTPException","httpStatus":404,"severity":"error","filePath":"invokeai/app/api/routers/videos.py","lineNumber":729,"sourceCode":"    return Response(\n        thumbnail,\n        media_type=\"image/webp\",\n        headers={\"Cache-Control\": _get_video_cache_control()},\n    )\n\n\n@videos_router.get(\"/i/{video_name}/urls\", operation_id=\"get_video_urls\", response_model=VideoUrlsDTO)\ndef get_video_urls(\n    current_user: CurrentUserOrDefault,\n    video_name: str = PathParam(description=\"The name of the video whose URL to get\"),\n) -> VideoUrlsDTO:\n    _assert_video_read_access(video_name, current_user)\n    try:\n        video_url = ApiDependencies.invoker.services.videos.get_url(video_name)\n        thumbnail_url = ApiDependencies.invoker.services.videos.get_url(video_name, thumbnail=True)\n        return VideoUrlsDTO(video_name=video_name, video_url=video_url, thumbnail_url=thumbnail_url)\n    except Exception:\n        raise HTTPException(status_code=404)\n\n\n@videos_router.get(\"/\", operation_id=\"list_video_dtos\", response_model=OffsetPaginatedResults[VideoDTO])\ndef list_video_dtos(\n    current_user: CurrentUserOrDefault,\n    video_origin: Optional[ResourceOrigin] = Query(default=None, description=\"The origin of videos to list.\"),\n    categories: Optional[list[ImageCategory]] = Query(default=None, description=\"The categories of video to include.\"),\n    is_intermediate: Optional[bool] = Query(default=None, description=\"Whether to list intermediate videos.\"),\n    board_id: Optional[str] = Query(\n        default=None,\n        description=\"The board id to filter by. Use 'none' to find videos without a board.\",\n    ),\n    # Bounds matter: these flow verbatim into SQL, and a negative LIMIT means\n    # *unlimited* in SQLite — one request would materialize every video row.\n    offset: int = Query(default=0, ge=0, description=\"The page offset\"),\n    limit: int = Query(default=10, ge=0, le=MAX_PAGE_SIZE, description=\"The number of videos per page\"),\n    order_dir: SQLiteDirection = Query(default=SQLiteDirection.Descending, description=\"The order of sort\"),\n    starred_first: bool = Query(default=True, description=\"Whether to sort by starred videos first\"),","sourceCodeStart":711,"sourceCodeEnd":747,"githubUrl":"https://github.com/invoke-ai/InvokeAI/blob/0b6a024f2ff6a86bfb953dcdb9cc504ef7397a06/invokeai/app/api/routers/videos.py#L711-L747","documentation":"HTTP 404 raised by get_video_urls in the videos router when generating URLs for a video fails for any reason. The handler wraps ApiDependencies.invoker.services.videos.get_url (for both video and thumbnail) in try/except and converts any exception into a bare 404. Because the except clause swallows the original exception, the underlying cause (missing file, bad service config) is lost.","triggerScenarios":"GET /api/v1/videos/{video_name}/urls where the video name does not exist, the underlying file was deleted from disk, or the video URL service throws while resolving the URL.","commonSituations":"Client holds a stale video_name after the video was deleted or pruned; file storage volume not mounted so the file is missing; typo'd video name (wrong extension or ID vs name confusion); database record exists but the binary is gone.","solutions":["Verify the video_name exists by listing videos (GET /api/v1/videos) before requesting URLs","Check the video file actually exists in the configured storage location (mounted volume, correct path)","Log the swallowed exception server-side to see the real cause (the handler hides it)","Restore the missing file or delete the orphaned record, then retry"],"exampleFix":"// before\nvideo_url = ApiDependencies.invoker.services.videos.get_url(bad_name)\n// after\nnames = client.list_videos().items\nif any(v.video_name == bad_name for v in names):\n    urls = client.get_video_urls(bad_name)","handlingStrategy":"try-catch","validationCode":"// verify video exists before fetching URLs\nconst list = await fetch(`${base}/api/v1/videos?limit=100`).then(r => r.json());\nconst exists = list.items.some(v => v.video_name === videoName);\nif (!exists) throw new Error(`video ${videoName} not in library`);","typeGuard":null,"tryCatchPattern":"try {\n  const urls = await fetch(`${base}/api/v1/videos/${encodeURIComponent(videoName)}/urls`);\n  if (!urls.ok) {\n    if (urls.status === 404) console.warn(`video ${videoName} missing; refreshing list`);\n    return null;\n  }\n  return await urls.json();\n} catch (e) { log(e); return null; }","preventionTips":["Resolve video names from a fresh list call rather than cached references","Confirm storage volumes are mounted and files exist on disk","Add server-side logging inside the bare except so 404s are diagnosable"],"tags":["http-404","rest-api","video","invokeai"],"backgroundTag":"resource-not-found-404","analyzedSha":"0b6a024f2ff6a86bfb953dcdb9cc504ef7397a06","analyzedAt":"2026-08-29T04:46:49.967Z","schemaVersion":2},"datasetVersion":"2026-08-29T07:17:48.351Z"}