{"record":{"id":"1bc97bd5003ca5f4","repo":"SixLabors/ImageSharp","slug":"cannot-remove-last-frame","errorCode":null,"errorMessage":"Cannot remove last frame.","messagePattern":"Cannot remove last frame\\.","errorType":"exception","errorClass":"InvalidOperationException","httpStatus":null,"severity":"error","filePath":"src/ImageSharp/ImageFrameCollection{TPixel}.cs","lineNumber":208,"sourceCode":"    public ImageFrame<TPixel> AddFrame(TPixel[] source)\n    {\n        Guard.NotNull(source, nameof(source));\n\n        return this.AddFrame(source.AsSpan());\n    }\n\n    /// <summary>\n    /// Removes the frame at the specified index and frees all freeable resources associated with it.\n    /// </summary>\n    /// <param name=\"index\">The zero-based index of the frame to remove.</param>\n    /// <exception cref=\"InvalidOperationException\">Cannot remove last frame.</exception>\n    public override void RemoveFrame(int index)\n    {\n        this.EnsureNotDisposed();\n\n        if (index == 0 && this.Count == 1)\n        {\n            throw new InvalidOperationException(\"Cannot remove last frame.\");\n        }\n\n        ImageFrame<TPixel> frame = this.frames[index];\n        this.frames.RemoveAt(index);\n        frame.Dispose();\n    }\n\n    /// <inheritdoc />\n    public override bool Contains(ImageFrame frame)\n    {\n        this.EnsureNotDisposed();\n\n        return frame is ImageFrame<TPixel> specific && this.frames.Contains(specific);\n    }\n\n    /// <summary>\n    /// Determines whether the <seealso cref=\"ImageFrameCollection{TPixel}\"/> contains the <paramref name=\"frame\"/>.\n    /// </summary>","sourceCodeStart":190,"sourceCodeEnd":226,"githubUrl":"https://github.com/SixLabors/ImageSharp/blob/59ce6af6fc29027cda277ef62d4d1694a8acce91/src/ImageSharp/ImageFrameCollection{TPixel}.cs#L190-L226","documentation":"RemoveFrame refuses to remove the only remaining frame because every Image must retain at least one frame. Attempting to remove frame 0 when Count == 1 would leave the image in an invalid, frame-less state, so InvalidOperationException is thrown.","triggerScenarios":"Calling image.Frames.RemoveFrame(0) on an image that contains exactly one frame, or looping RemoveFrame over all indexes including the last remaining one.","commonSituations":"Animation-trimming code that removes frames without stopping before the last one, or generic 'remove processed frames' logic that doesn't special-case the final frame.","solutions":["Check Count == 1 (or index == 0 && Count == 1) before removing and stop the loop at the last frame.","Iterate down to Count - 1 frames only: for (int i = image.Frames.Count - 1; i > 0; i--) RemoveFrame(i).","If a truly empty result is required, construct a new image instead of stripping the last frame."],"exampleFix":"// before\nfor (int i = image.Frames.Count - 1; i >= 0; i--)\n{\n    image.Frames.RemoveFrame(i);\n}\n// after\nfor (int i = image.Frames.Count - 1; i > 0; i--)\n{\n    image.Frames.RemoveFrame(i);\n}","handlingStrategy":"try-catch","validationCode":"if (image.Frames.Count <= 1 && index == 0) return; // refuse to strip the last frame","typeGuard":"static bool CanRemoveFrame(Image<TPixel> image, int index) => image.Frames.Count > 1 && index >= 0 && index < image.Frames.Count;","tryCatchPattern":"try { image.Frames.RemoveFrame(index); }\ncatch (InvalidOperationException ex) when (image.Frames.Count == 1) { /* keep the last frame */ }","preventionTips":["Stop frame-removal loops at Count > 0, leaving one frame alive.","Check Count before RemoveFrame when index is computed dynamically.","Build new images rather than emptying existing ones when a frameless result is the goal."],"tags":["image","frames","invalid-operation"],"backgroundTag":"invalid-state-transition","analyzedSha":"59ce6af6fc29027cda277ef62d4d1694a8acce91","analyzedAt":"2026-09-13T18:34:59.331Z","contentChangedAt":"2026-09-13T18:34:59.331Z","schemaVersion":2},"datasetVersion":"2026-09-16T04:17:20.429Z"}