{"record":{"id":"836e097b808d500d","repo":"NickeManarin/ScreenToGif","slug":"impossible-to-capture-the-manual-screenshot-836e09","errorCode":null,"errorMessage":"Impossible to capture the manual screenshot.","messagePattern":"Impossible to capture the manual screenshot\\.","errorType":"exception","errorClass":"Exception","httpStatus":null,"severity":"error","filePath":"ScreenToGif/Windows/Recorder.xaml.cs","lineNumber":1003,"sourceCode":"            catch (Exception ex)\n            {\n                LogWriter.Log(ex, \"Impossible to start the screencasting.\");\n                ErrorDialog.Ok(Title, LocalizationHelper.Get(\"S.Recorder.Warning.CaptureNotPossible\"), ex.Message, ex);\n                return;\n            }\n        }\n\n        #region Take the screenshot\n\n        try\n        {\n            var limit = 0;\n            do\n            {\n                FrameCount = await Capture.ManualCaptureAsync(new FrameInfo(RecordClicked, KeyList), UserSettings.All.ShowCursor);\n\n                if (limit > 5)\n                    throw new Exception(\"Impossible to capture the manual screenshot.\");\n\n                limit++;\n            }\n            while (FrameCount == 0);\n\n            KeyList.Clear();\n\n            DisplayTimer.ManuallyCapturedCount++;\n            CommandManager.InvalidateRequerySuggested();\n        }\n        catch (GraphicsConfigurationException g)\n        {\n            IsRecording = false;\n\n            LogWriter.Log(g, \"Impossible to start the recording due to wrong graphics adapter.\");\n            GraphicsConfigurationDialog.Ok(g, _viewModel.CurrentMonitor);\n        }\n        catch (Exception e)","sourceCodeStart":985,"sourceCodeEnd":1021,"githubUrl":"https://github.com/NickeManarin/ScreenToGif/blob/a4d0a67c2131cd048ceec86cd40afc2f1a06f2fd/ScreenToGif/Windows/Recorder.xaml.cs#L985-L1021","documentation":"Thrown by the Recorder when ManualCaptureAsync repeatedly returns zero captured frames. The code loops calling Capture.ManualCaptureAsync up to ~6 times (limit 0..5) while FrameCount stays 0; once the retry budget is exhausted the generic Exception is raised. It is a safety valve for a capture pipeline (Desktop Duplication or BitBlt) that is alive but producing no usable bitmap frames.","triggerScenarios":"The user triggers a manual-capture frame (CaptureFrequencies.Manual) and Capture.ManualCaptureAsync() returns 0 every iteration for more than 5 attempts. This happens when the underlying capture device returns an empty/stale frame texture (Desktop Duplication mode) or BitBlt yields an empty region (ImageCapture/CachedCapture), e.g. the recorded monitor is asleep, GPU reset, exclusive-fullscreen app, or RDP session without desktop composition.","commonSituations":"Recording on a monitor that went to sleep / powered off mid-session; a laptop lid closed (display disconnected); running inside an RDP/virtual display session where DXGI Desktop Duplication has no AcquireNextFrame; a driver crash or TDR resetting the GPU; recording a fullscreen DirectX/Vulkan game that blocks BitBlt; DPI/credential (UAC prompt on secure desktop) screens blocking capture.","solutions":["Switch the capture backend: turn off 'Use desktop duplication' in Settings > Extras so the recorder falls back to BitBlt (ImageCapture/CachedCapture) in PrepareCapture.","Wake all displays / reopen the laptop lid and reselect the target monitor in the recorder, then retry — a disconnected or sleeping monitor yields zero frames from DXGI.","Stop recording exclusive-fullscreen apps or borderless-window apps that block desktop capture; run the target window in windowed mode.","Update or roll back the graphics driver — a TDR/driver crash leaves the Desktop Duplication output returning empty frames.","If on RDP, enable RemoteFX/stable virtual GPU, or record on the physical host instead — headless/limited RDP sessions cannot produce Desktop Duplication frames.","Increase the retry budget or add a small delay between ManualCaptureAsync attempts so a transient GPU stall can clear."],"exampleFix":"// before\nvar limit = 0;\ndo\n{\n    FrameCount = await Capture.ManualCaptureAsync(new FrameInfo(RecordClicked, KeyList), UserSettings.All.ShowCursor);\n    if (limit > 5)\n        throw new Exception(\"Impossible to capture the manual screenshot.\");\n    limit++;\n}\nwhile (FrameCount == 0);\n\n// after: bounded retries with backoff + clear cause before giving up\nvar limit = 0;\ndo\n{\n    FrameCount = await Capture.ManualCaptureAsync(new FrameInfo(RecordClicked, KeyList), UserSettings.All.ShowCursor);\n    if (FrameCount == 0)\n    {\n        if (++limit > 10)\n            throw new CaptureEmptyFrameException(\"Manual capture returned no frame after retries. Backend: \" + Capture.GetType().Name);\n        await Task.Delay(50);\n    }\n}\nwhile (FrameCount == 0);","handlingStrategy":"retry","validationCode":"// Before starting manual capture, confirm the backend can produce a frame.\nif (Capture == null)\n    throw new InvalidOperationException(\"Capture backend not initialized.\");\n\nif (_viewModel?.CurrentMonitor == null || _viewModel.Monitors.Count == 0)\n    throw new InvalidOperationException(\"No target monitor selected for capture.\");\n\n// Optional: verify the target monitor is still powered/connected.\nvar alive = MonitorHelper.AllMonitors.Any(m => m.DeviceName == Capture.DeviceName);\nif (!alive)\n    throw new InvalidOperationException(\"Target monitor no longer available.\");","typeGuard":"// Narrow the capture backend so retry/backoff policy is backend-aware.\nstatic bool IsDirectCapture(ICapture capture) => capture is DirectCapture;\n\nif (IsDirectCapture(Capture))\n{\n    // DXGI: longer backoff, fewer retries (GPU stall clears slowly).\n}","tryCatchPattern":"// Catch the retry-exhausted state separately from GraphicsConfigurationException.\ntry\n{\n    // ... capture loop ...\n}\ncatch (GraphicsConfigurationException g)\n{\n    IsRecording = false;\n    LogWriter.Log(g, \"Wrong graphics adapter for capture.\");\n    GraphicsConfigurationDialog.Ok(g, _viewModel.CurrentMonitor);\n}\ncatch (Exception e) when (e.Message.Contains(\"manual screenshot\"))\n{\n    IsRecording = false;\n    LogWriter.Log(e, \"Manual capture retries exhausted.\");\n    // Offer the user a backend switch instead of a hard stop.\n    if (UserSettings.All.UseDesktopDuplication)\n        ErrorDialog.Ok(Title, \"Capture failed\", \"Disable Desktop Duplication in Settings and retry.\", e);\n}","preventionTips":["Keep at least one display awake and connected during manual-capture sessions.","Prefer BitBlt (disable Desktop Duplication) on RDP or unstable GPU setups.","Add a small delay between ManualCaptureAsync retries to let transient GPU stalls clear.","Log the capture backend type and FrameCount per attempt so failures are diagnosable."],"tags":["capture","dxgi","desktop-duplication","bitblt","manual-capture","graphics","retry-loop"],"backgroundTag":null,"analyzedSha":"a4d0a67c2131cd048ceec86cd40afc2f1a06f2fd","analyzedAt":"2026-08-13T11:12:06.147Z","schemaVersion":2},"datasetVersion":"2026-08-13T14:17:21.547Z"}