{"record":{"id":"975012ed373e8814","repo":"SixLabors/ImageSharp","slug":"ani-resolution-variants-must-use-the-same-frame-delay","errorCode":null,"errorMessage":"ANI resolution variants must use the same frame delay.","messagePattern":"ANI resolution variants must use the same frame delay\\.","errorType":"exception","errorClass":"ImageFormatException","httpStatus":null,"severity":"error","filePath":"src/ImageSharp/Formats/Ani/AniEncoderCore.cs","lineNumber":118,"sourceCode":"\n            if (bitmapResources && groupSize > 1)\n            {\n                // Only ICO/CUR directories can contain multiple resolution variants in one physical resource.\n                throw new ImageFormatException(\"ANI bitmap resources cannot contain resolution variants.\");\n            }\n\n            // All variants share one animation step, which requires one child format and one rate value.\n            for (int i = 1; i < groupSize; i++)\n            {\n                AniFrameMetadata current = image.Frames[frameIndex + i].Metadata.GetAniMetadata();\n                if (current.FrameFormat != metadata.FrameFormat)\n                {\n                    throw new ImageFormatException(\"ANI resolution variants must use the same embedded format.\");\n                }\n\n                if (current.FrameDelay != metadata.FrameDelay)\n                {\n                    throw new ImageFormatException(\"ANI resolution variants must use the same frame delay.\");\n                }\n            }\n\n            uint frameDelay = metadata.FrameDelay is 0 ? displayRate : metadata.FrameDelay;\n            hasVariableRates |= frameDelay != displayRate;\n            maxGroupSize = Math.Max(maxGroupSize, groupSize);\n            groupCount++;\n            frameIndex += groupSize;\n        }\n\n        // Icon-based ANI files leave global geometry and pixel layout at zero because each ICO/CUR entry owns those values.\n        AniHeader header = new()\n        {\n            BytesInHeader = AniHeader.Size,\n            FrameCount = (uint)groupCount,\n            StepCount = (uint)groupCount,\n            Width = bitmapResources ? imageMetadata.Width is 0 ? (uint)image.Width : imageMetadata.Width : 0,\n            Height = bitmapResources ? imageMetadata.Height is 0 ? (uint)image.Height : imageMetadata.Height : 0,","sourceCodeStart":100,"sourceCodeEnd":136,"githubUrl":"https://github.com/SixLabors/ImageSharp/blob/59ce6af6fc29027cda277ef62d4d1694a8acce91/src/ImageSharp/Formats/Ani/AniEncoderCore.cs#L100-L136","documentation":"Frames grouped as resolution variants of one resource share a single animation step and therefore a single rate value. The encoder requires every frame in a group to declare the identical AniFrameMetadata.FrameDelay; a differing delay would be unrepresentable in the ANI rate table, so it throws ImageFormatException.","triggerScenarios":"Encoding an ANI where consecutive frames with the same positive SequenceNumber (one variant group) have differing FrameDelay values.","commonSituations":"Varying per-frame timing across resolution variants of the same cursor image; delays copied from per-frame sources (e.g. GIF) without normalization before ANI encoding.","solutions":["Assign the same FrameDelay to all frames sharing a SequenceNumber.","Set FrameDelay = 0 on variant frames so the group falls back to the shared display rate.","Split variants with different timing into separate animation steps (distinct SequenceNumbers)."],"exampleFix":"// before\nf0.Metadata.GetAniMetadata().FrameDelay = 100;\nf1.Metadata.GetAniMetadata().FrameDelay = 200; // same variant group\n// after\nf0.Metadata.GetAniMetadata().FrameDelay = 100;\nf1.Metadata.GetAniMetadata().FrameDelay = 100;","handlingStrategy":"validation","validationCode":"var delays = image.Frames\n    .Where(f => f.Metadata.GetAniMetadata().SequenceNumber == targetSeq)\n    .Select(f => f.Metadata.GetAniMetadata().FrameDelay).Distinct().ToList();\nif (delays.Count > 1) throw new InvalidOperationException(\"Variant group delays differ.\");","typeGuard":null,"tryCatchPattern":"try { image.SaveAsAni(stream); }\ncatch (ImageFormatException ex) { Log(ex.Message); }","preventionTips":["Set FrameDelay = 0 on all frames of a variant group to inherit the shared rate.","Copy per-frame delays from GIF only after normalizing for ANI grouping.","Include FrameDelay in any metadata normalization pass."],"tags":["image-encoding","ani","metadata-validation"],"backgroundTag":"schema-validation-failed","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"}