{"record":{"id":"7f8b58dbc133ab6d","repo":"sgl-project/sglang","slug":"video-not-found","errorCode":null,"errorMessage":"Video not found","messagePattern":"Video not found","errorType":"http","errorClass":"HTTPException","httpStatus":404,"severity":"warning","filePath":"python/sglang/multimodal_gen/runtime/entrypoints/openai/video_api.py","lineNumber":915,"sourceCode":"    return VideoResponse(**job)\n\n\n@router.get(\"\", response_model=VideoListResponse)\nasync def list_videos(\n    after: Optional[str] = Query(None),\n    limit: Optional[int] = Query(None, ge=1, le=100),\n    order: Optional[str] = Query(\"desc\"),\n):\n    jobs = await VIDEO_STORE.list_page(after=after, limit=limit, order=order)\n    items = [VideoResponse(**job) for job in jobs]\n    return VideoListResponse(data=items)\n\n\n@router.get(\"/{video_id}\", response_model=VideoResponse)\nasync def retrieve_video(video_id: str = Path(...)):\n    job = await VIDEO_STORE.get(video_id)\n    if not job:\n        raise HTTPException(status_code=404, detail=\"Video not found\")\n    return VideoResponse(**job)\n\n\n# TODO: support aborting a job.\n@router.delete(\"/{video_id}\", response_model=VideoResponse)\nasync def delete_video(video_id: str = Path(...)):\n    job = await VIDEO_STORE.pop(video_id)\n    if not job:\n        raise HTTPException(status_code=404, detail=\"Video not found\")\n    # Mark as deleted in response semantics\n    job[\"status\"] = \"deleted\"\n    return VideoResponse(**job)\n\n\ndef _select_video_variant_path(job: dict, variant: str | None) -> str | None:\n    file_paths = job.get(\"file_paths\")\n    if file_paths:\n        try:","sourceCodeStart":897,"sourceCodeEnd":933,"githubUrl":"https://github.com/sgl-project/sglang/blob/0132848349585cfe6aae51c4941cbae872505f8a/python/sglang/multimodal_gen/runtime/entrypoints/openai/video_api.py#L897-L933","documentation":"HTTP 404 from GET /v1/videos/{video_id} when the in-memory VIDEO_STORE contains no job with that id. The store only holds jobs created by this server process (or its configured backend), so unknown, expired, or restarted-away ids all miss.","triggerScenarios":"GET /v1/videos/{id} with a typo'd or truncated id, an id from a previous server process (store lost on restart), or a job evicted after the store's retention policy/capacity limit.","commonSituations":"Server restarted between creating the job and polling status; copy-pasting an id with whitespace or missing characters; polling long after completion when the entry was evicted; hitting a different server instance behind a load balancer than the one that created the job.","solutions":["Verify the id matches exactly what the POST /v1/videos response returned","If the server restarted, resubmit the generation — in-memory state is not persisted","Check VIDEO_STORE retention/capacity settings and poll sooner or persist ids externally","Ensure sticky routing / same host when multiple server replicas serve the API"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"null","typeGuard":null,"tryCatchPattern":"const job = await get(id).catch(e => { if (e.status === 404) return null; throw e; });\nif (!job) { /* resubmit or surface 'expired' */ }","preventionTips":["Persist the id from the create response immediately","Poll promptly after creation; don't assume jobs persist across restarts","Use consistent host/sticky routing when multiple replicas exist"],"tags":["http-404","video-generation","job-not-found","polling"],"backgroundTag":"resource-not-found","analyzedSha":"0132848349585cfe6aae51c4941cbae872505f8a","analyzedAt":"2026-08-28T05:10:05.995Z","schemaVersion":2},"datasetVersion":"2026-08-28T06:17:29.519Z"}