{"record":{"id":"888ba7971b00a23f","repo":"SixLabors/ImageSharp","slug":"ani-bitmap-resources-require-exactly-one-color-plane","errorCode":null,"errorMessage":"ANI bitmap resources require exactly one color plane.","messagePattern":"ANI bitmap resources require exactly one color plane\\.","errorType":"exception","errorClass":"ImageFormatException","httpStatus":null,"severity":"error","filePath":"src/ImageSharp/Formats/Ani/AniEncoderCore.cs","lineNumber":70,"sourceCode":"\n        AniMetadata imageMetadata = image.Metadata.GetAniMetadata();\n        AniFrameMetadata firstMetadata = image.Frames.RootFrame.Metadata.GetAniMetadata();\n        AniFrameFormat firstFormat = firstMetadata.FrameFormat;\n        bool bitmapResources = firstFormat is AniFrameFormat.Bmp;\n        bool writeSequence = imageMetadata.Flags.HasFlag(AniHeaderFlags.ContainsSequence);\n        uint displayRate = firstMetadata.FrameDelay is 0 ? imageMetadata.DisplayRate : firstMetadata.FrameDelay;\n        bool hasVariableRates = false;\n        int groupCount = 0;\n        int maxGroupSize = 1;\n\n        if (bitmapResources && imageMetadata.BitCount is not (0 or 1 or 2 or 4 or 8 or 16 or 24 or 32))\n        {\n            throw new ImageFormatException(\"ANI bitmap resources require a supported bit depth.\");\n        }\n\n        if (bitmapResources && imageMetadata.Planes is not (0 or 1))\n        {\n            throw new ImageFormatException(\"ANI bitmap resources require exactly one color plane.\");\n        }\n\n        // This validation pass derives the fixed ANI header and largest icon directory without allocating a grouping graph.\n        // Encoding repeats the linear grouping scan below, trading a cheap pass for zero per-group collections.\n        for (int frameIndex = 0; frameIndex < image.Frames.Count;)\n        {\n            AniFrameMetadata metadata = image.Frames[frameIndex].Metadata.GetAniMetadata();\n            int groupSize = 1;\n\n            if (metadata.FrameFormat is not (AniFrameFormat.Ico or AniFrameFormat.Cur or AniFrameFormat.Bmp))\n            {\n                // FrameFormat is public metadata and therefore must be validated before any container bytes are written.\n                throw new ImageFormatException(\"ANI contains an unsupported embedded frame format.\");\n            }\n\n            // Positive sequence numbers group adjacent resolution variants; non-positive values form independent steps.\n            if (metadata.SequenceNumber > 0)\n            {","sourceCodeStart":52,"sourceCodeEnd":88,"githubUrl":"https://github.com/SixLabors/ImageSharp/blob/59ce6af6fc29027cda277ef62d4d1694a8acce91/src/ImageSharp/Formats/Ani/AniEncoderCore.cs#L52-L88","documentation":"Thrown by AniEncoderCore.Encode when encoding with bitmapResources enabled and the ANI metadata's Planes value is not 0 (unset/default) or 1. ICO/BMP resources embedded in an ANI must declare exactly one color plane, so any other plane count is rejected up front.","triggerScenarios":"Calling image.SaveAsAni (or Image.Save with AniEncoder) with bitmapResources configured while AniMetadata.Planes holds a value other than 0 or 1, e.g. 2 or 4.","commonSituations":"Copying Planes metadata from multi-plane sources, manually setting incorrect plane counts, misunderstanding the ICO one-plane requirement.","solutions":["Set AniMetadata.Planes to 1 (or 0 to let the encoder default it) before saving as ANI.","Remove the explicit Planes override so the default is used.","Encode frames as ICO/CUR resources instead of bitmapResources if multi-plane data is genuinely required."],"exampleFix":"// before\nmetadata.Planes = 2;\nawait image.SaveAsAniAsync(stream, aniOptions);\n// after\nmetadata.Planes = 1;\nawait image.SaveAsAniAsync(stream, aniOptions);","handlingStrategy":"validation","validationCode":"bool planesOk = aniMetadata.Planes is 0 or 1;\nif (!planesOk) throw new InvalidOperationException(\"ANI bitmap resources need Planes = 1.\");","typeGuard":null,"tryCatchPattern":"try { await image.SaveAsAniAsync(stream, options); } catch (ImageFormatException ex) when (ex.Message.Contains(\"color plane\")) { /* fix metadata */ }","preventionTips":["Leave Planes at its default (0) unless you know you need 1","Never copy Planes metadata from non-ICO sources into ANI bitmap options","Validate ANI encoder options with unit tests covering bitmapResources mode"],"tags":["image-encoding","ani","planes","configuration","validation"],"backgroundTag":"invalid-config-value","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"}