{"record":{"id":"12de3fd8b8e5b2ab","repo":"invoke-ai/InvokeAI","slug":"failed-to-update-video","errorCode":null,"errorMessage":"Failed to update video","messagePattern":"Failed to update video","errorType":"http","errorClass":"HTTPException","httpStatus":400,"severity":"error","filePath":"invokeai/app/api/routers/videos.py","lineNumber":484,"sourceCode":"    return DeleteVideosResult(\n        deleted_videos=list(deleted_videos),\n        failed_videos=list(failed_videos),\n        affected_boards=list(affected_boards),\n    )\n\n\n# Sync for the same reason as delete_video: the update is a blocking SQLite write.\n@videos_router.patch(\"/i/{video_name}\", operation_id=\"update_video\", response_model=VideoDTO)\ndef update_video(\n    current_user: CurrentUserOrDefault,\n    video_name: str = PathParam(description=\"The name of the video to update\"),\n    video_changes: VideoRecordChanges = Body(description=\"The changes to apply to the video\"),\n) -> VideoDTO:\n    _assert_video_owner(video_name, current_user)\n    try:\n        return ApiDependencies.invoker.services.videos.update(video_name, video_changes)\n    except Exception:\n        raise HTTPException(status_code=400, detail=\"Failed to update video\")\n\n\n@videos_router.get(\"/i/{video_name}\", operation_id=\"get_video_dto\", response_model=VideoDTO)\ndef get_video_dto(\n    current_user: CurrentUserOrDefault,\n    video_name: str = PathParam(description=\"The name of video to get\"),\n) -> VideoDTO:\n    _assert_video_read_access(video_name, current_user)\n    try:\n        return ApiDependencies.invoker.services.videos.get_dto(video_name)\n    except VideoRecordNotFoundException:\n        # See get_image_dto: this is the 404 a workflow's video field drops its reference on,\n        # so only a genuinely missing record may produce it.\n        raise HTTPException(status_code=404)\n\n\n@videos_router.get(\n    \"/i/{video_name}/metadata\", operation_id=\"get_video_metadata\", response_model=Optional[MetadataField]","sourceCodeStart":466,"sourceCodeEnd":502,"githubUrl":"https://github.com/invoke-ai/InvokeAI/blob/0b6a024f2ff6a86bfb953dcdb9cc504ef7397a06/invokeai/app/api/routers/videos.py#L466-L502","documentation":"Raised by update_video (PATCH videos/i/{video_name}) as HTTP 400 when the service's update(video_name, video_changes) call raises any exception. It means the record exists and the caller passed the ownership check, but applying the changes failed — typically due to invalid field values in VideoRecordChanges or a database failure. The original exception is swallowed, so the server log is needed to learn the real cause.","triggerScenarios":"PATCH with a VideoRecordChanges payload containing invalid values (e.g. bad board_id, out-of-range fields), a DB write failure, or a concurrent modification conflict; also any unexpected exception inside the service update path.","commonSituations":"Client sending stale board_id after the board was deleted; malformed PATCH body shape; SQLite lock/disk issues; API version drift where the client sends fields the current schema rejects.","solutions":["Inspect the server logs for the underlying exception raised by videos.update().","Validate the VideoRecordChanges payload: ensure board_id references an existing board and only allowed fields are set.","Re-fetch the video DTO (GET videos/i/{video_name}) and retry with a fresh, minimal changes object.","Check database health (locks, disk space) if the payload is valid but the error persists."],"exampleFix":"// before: blind full-object patch with stale fields\nawait api.updateVideo(name, { ...oldDto, board_id: staleBoardId });\n// after\nconst fresh = await api.getVideoDto(name);\nawait api.updateVideo(name, { board_id: await ensureBoardExists(targetBoardId) });","handlingStrategy":"validation","validationCode":"function validChanges(c) {\n  return c && Object.keys(c).length > 0 &&\n    (c.board_id === undefined || c.board_id === 'none' || typeof c.board_id === 'string');\n}\nif (!validChanges(changes)) throw new Error('Invalid VideoRecordChanges');","typeGuard":"function isVideoRecordChanges(v) {\n  return typeof v === 'object' && v !== null &&\n    !('video_name' in v) && !('created_at' in v); // changes object, not a full DTO\n}","tryCatchPattern":"try {\n  return await api.updateVideo(name, changes);\n} catch (e) {\n  if (e.response?.status === 400) {\n    const fresh = await api.getVideoDto(name);\n    return api.updateVideo(name, { board_id: changes.board_id }); // minimal retry\n  }\n  throw e;\n}","preventionTips":["Patch with minimal, fresh change objects — never spread stale full DTOs.","Resolve board_id against the live board list before patching.","Read server logs when 400 persists with an apparently valid payload."],"tags":["http-400","bad-request","rest-api","video","fastapi"],"backgroundTag":"bad-request-payload","analyzedSha":"0b6a024f2ff6a86bfb953dcdb9cc504ef7397a06","analyzedAt":"2026-08-29T04:46:49.967Z","schemaVersion":2},"datasetVersion":"2026-08-29T07:17:48.351Z"}