SixLabors/ImageSharp · error · InvalidOperationException
Cannot remove last frame.
Error message
Cannot remove last frame.
What it means
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.
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.
Example fix
// before
for (int i = image.Frames.Count - 1; i >= 0; i--)
{
image.Frames.RemoveFrame(i);
}
// after
for (int i = image.Frames.Count - 1; i > 0; i--)
{
image.Frames.RemoveFrame(i);
} Defensive patterns
Strategy: try-catch
Validate before calling
if (image.Frames.Count <= 1 && index == 0) return; // refuse to strip the last frame
Type guard
static bool CanRemoveFrame(Image<TPixel> image, int index) => image.Frames.Count > 1 && index >= 0 && index < image.Frames.Count;
Try / catch
try { image.Frames.RemoveFrame(index); }
catch (InvalidOperationException ex) when (image.Frames.Count == 1) { /* keep the last frame */ } Prevention
- 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.
When it happens
Trigger: Calling image.Frames.RemoveFrame(0) on an image that contains exactly one frame, or looping RemoveFrame over all indexes including the last remaining one.
Common situations: 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.
Understand the failure class
Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.
Related errors
- Must not be empty.
- No encoder was found for extension
- No encoder was found for extension
- Source ICC profile is missing.
- Target ICC profile is missing.
AI-assisted analysis of SixLabors/ImageSharp@59ce6af6fc (2026-09-13).
Data as JSON: /api/errors/1bc97bd5003ca5f4.
Report an issue: GitHub.
Appendix: source
Thrown at src/ImageSharp/ImageFrameCollection{TPixel}.cs:208
public ImageFrame<TPixel> AddFrame(TPixel[] source)
{
Guard.NotNull(source, nameof(source));
return this.AddFrame(source.AsSpan());
}
/// <summary>
/// Removes the frame at the specified index and frees all freeable resources associated with it.
/// </summary>
/// <param name="index">The zero-based index of the frame to remove.</param>
/// <exception cref="InvalidOperationException">Cannot remove last frame.</exception>
public override void RemoveFrame(int index)
{
this.EnsureNotDisposed();
if (index == 0 && this.Count == 1)
{
throw new InvalidOperationException("Cannot remove last frame.");
}
ImageFrame<TPixel> frame = this.frames[index];
this.frames.RemoveAt(index);
frame.Dispose();
}
/// <inheritdoc />
public override bool Contains(ImageFrame frame)
{
this.EnsureNotDisposed();
return frame is ImageFrame<TPixel> specific && this.frames.Contains(specific);
}
/// <summary>
/// Determines whether the <seealso cref="ImageFrameCollection{TPixel}"/> contains the <paramref name="frame"/>.
/// </summary>View on GitHub (pinned to 59ce6af6fc)