{"record":{"id":"fbf8a6f2e85ddfc3","repo":"mudler/LocalAI","slug":"request-was-cancelled","errorCode":null,"errorMessage":"request was cancelled","messagePattern":"request was cancelled","errorType":"exception","errorClass":"RuntimeError","httpStatus":null,"severity":"warning","filePath":"backend/python/longcat-video/backend.py","lineNumber":729,"sourceCode":"                **common,\n            )\n        else:\n            width, height = validate_dimensions(request.width, request.height)\n            output, latent = self.pipeline.generate_at2v(\n                height=height,\n                width=width,\n                **common,\n            )\n\n        video = self._frames_to_pil(output[0])\n        width, height = video[0].size\n        current_video = video\n        reference_latent = latent[:, :, :1].clone()\n        all_frames = list(video)\n\n        for segment in range(1, segments):\n            if hasattr(context, \"is_active\") and not context.is_active():\n                raise RuntimeError(\"request was cancelled\")\n            print(\n                f\"Generating avatar segment {segment + 1}/{segments}\", file=sys.stderr\n            )\n            audio_start += segment_frames - conditioning_frames\n            output, latent = self.pipeline.generate_avc(\n                video=current_video,\n                video_latent=latent,\n                prompt=request.prompt,\n                negative_prompt=negative_prompt,\n                height=height,\n                width=width,\n                num_frames=segment_frames,\n                num_cond_frames=conditioning_frames,\n                num_inference_steps=steps,\n                text_guidance_scale=text_guidance,\n                audio_guidance_scale=audio_guidance,\n                generator=generator,\n                output_type=\"both\",","sourceCodeStart":711,"sourceCodeEnd":747,"githubUrl":"https://github.com/mudler/LocalAI/blob/44413a9d06bf5bc52ce088ba8ca74e5a2e8bee26/backend/python/longcat-video/backend.py#L711-L747","documentation":"RuntimeError raised inside the avatar segment loop when context.is_active() reports the gRPC context is no longer active — the client cancelled the call, its deadline expired, or the connection dropped. Because avatar generation is sequential over segments (each ~93 frames with 13 conditioning frames carried over), the loop checks cancellation between segments and aborts instead of burning GPU on a dead request. Unlike the ValueError checks, this is a server-side cancellation signal, not a client input error.","triggerScenarios":"Client calls context.cancel() or closes the stream mid-generation; per-call deadline (timeout) shorter than total generation time for all segments; network drop between client and backend.","commonSituations":"UI 'stop' button cancelling generation; deadline set for short requests reused on long multi-segment avatar jobs; load balancer idle-cutting long-running streams.","solutions":["Raise or remove the client-side deadline so it exceeds worst-case total generation time (segments x per-segment time)","Only cancel when truly intended; if it fires spuriously, keep the gRPC channel alive (heartbeats/keepalive) during long jobs","Treat this error as benign cleanup on the server; on the client map UNAVAILABLE/CANCELLED to a user-facing 'generation stopped' state"],"exampleFix":"# before\nresponse = stub.Video gen(timeout=30))  # too short for multi-segment\n\n# after\nresponse = stub.GenerateVideo(req, timeout=1800)  # deadline sized to full job","handlingStrategy":"try-catch","validationCode":"# No pre-validation possible (server-observed cancellation), but size the deadline:\n# estimated segments = ceil((audio_seconds * 25 - 93) / 80) + 1\n# deadline > segments * worst_case_seconds_per_segment + slack\nimport math\nsegments = max(1, math.ceil((audio_seconds * 25 - 93) / 80) + 1)\ntimeout = int(segments * 120) + 60","typeGuard":null,"tryCatchPattern":"try:\n    resp = stub.GenerateVideo(req, timeout=timeout)\nexcept grpc.RpcError as e:\n    if e.code() in (grpc.StatusCode.CANCELLED, grpc.StatusCode.DEADLINE_EXCEEDED):\n        log.info(\"generation cancelled/deadline — retry with longer timeout if wanted\")\n        # safe to retry: generation is stateless per request\n    else:\n        raise","preventionTips":["Set per-request deadlines from estimated job length, not a global constant","Enable gRPC keepalive on long-running generation streams"],"tags":["python","longcat-video","grpc","cancellation","streaming"],"backgroundTag":null,"analyzedSha":"44413a9d06bf5bc52ce088ba8ca74e5a2e8bee26","analyzedAt":"2026-08-15T10:13:50.291Z","schemaVersion":2},"datasetVersion":"2026-08-16T03:17:38.424Z"}