{"record":{"id":"d17c8d4ffdd24dcf","repo":"d2phap/ImageGlass","slug":"ige-native-codec-proxy-codecid-is-missing-one","errorCode":null,"errorMessage":"IGE: Native codec '{proxy.CodecId}' is missing one of the animation entry points.","messagePattern":"IGE: Native codec '(.+?)' is missing one of the animation entry points\\.","errorType":"exception","errorClass":"NotSupportedException","httpStatus":null,"severity":"error","filePath":"source/ImageGlass.Lib/Plugins/NativePluginAnimator.cs","lineNumber":71,"sourceCode":"    private readonly Lock _syncLock = new();\n\n    private DispatcherTimer _timer;\n\n\n    /// <summary>\n    /// Builds an animator for the current photo by crossing the ABI once to pull\n    /// animation traits and per-frame timing. Releases the native\n    /// <see cref=\"IGAnimationInfo\"/> back to the plugin before returning.\n    /// </summary>\n    public static NativePluginAnimator Create(NativeCodecProxy proxy,\n        PhotoMetadata metadata, CancellationToken token)\n    {\n        var codecApi = proxy.CodecApi;\n        if (codecApi->GetAnimationInfo == null\n            || codecApi->FreeAnimationInfo == null\n            || codecApi->DecodeAnimationFrame == null)\n        {\n            throw new NotSupportedException(\n                $\"IGE: Native codec '{proxy.CodecId}' is missing one of the animation entry points.\");\n        }\n\n        // PHASE 1: register cancellation, allocate a stack slot for the info struct.\n        var cancelHandle = PluginHostApiTable.RegisterCancellation(token);\n        IGAnimationInfo info = default;\n        var infoOwned = false;\n        try\n        {\n            IGStatus status;\n            try\n            {\n                fixed (char* pPath = metadata.FilePath)\n                {\n                    var pathRef = new IGStringRef { Data = pPath, Length = metadata.FilePath.Length };\n                    status = codecApi->GetAnimationInfo(pathRef, &info, (void*)cancelHandle);\n                }\n            }","sourceCodeStart":53,"sourceCodeEnd":89,"githubUrl":"https://github.com/d2phap/ImageGlass/blob/4a3c4feceffc5a8bb5e56ba836509634aaae47a9/source/ImageGlass.Lib/Plugins/NativePluginAnimator.cs#L53-L89","documentation":"Thrown by NativePluginAnimator.Create when the plugin codec API table is missing any of the three animation entry points: GetAnimationInfo, FreeAnimationInfo, or DecodeAnimationFrame. The codec loaded and can decode static rasters, but it does not implement the animation ABI, so the host cannot build a frame-by-frame animator for it.","triggerScenarios":"Produced at NativePluginAnimator.cs:71 when any of codecApi->GetAnimationInfo, codecApi->FreeAnimationInfo, or codecApi->DecodeAnimationFrame is null. Reached when the host tries to create an animator for an animated file (GIF/WEBP/AVIF sequence) backed by this plugin.","commonSituations":"A plugin that supports only static decode of an extension but is registered for an animated container (e.g. a WEBP plugin that does still images only); an older plugin built before the animation ABI existed; a partial plugin implementation; the host mistakenly routed an animated file to a static-only codec.","solutions":["Confirm the plugin implements animation decode; if it is static-only, do not register it for animated file types.","Rebuild the plugin against the current SDK so it exports GetAnimationInfo, FreeAnimationInfo, and DecodeAnimationFrame when it claims animation support.","Gate animator creation on a capability flag from the plugin manifest so the host never calls Create on a static-only codec.","Fall back to a built-in animator (SkiaAnimator) for the file if the plugin cannot animate."],"exampleFix":"// before\nif (codecApi->GetAnimationInfo == null || codecApi->FreeAnimationInfo == null || codecApi->DecodeAnimationFrame == null)\n    throw new NotSupportedException($\"IGE: Native codec '{proxy.CodecId}' is missing one of the animation entry points.\");\n\n// caller — only request an animator when the codec advertises animation support\nif (!proxy.SupportsAnimation)\n    throw new NotSupportedException($\"IGE: Native codec '{proxy.CodecId}' is missing one of the animation entry points.\");","handlingStrategy":"validation","validationCode":"// Only request an animator when the codec advertises animation support.\nif (!proxy.SupportsAnimation)\n    throw new NotSupportedException($\"Codec {proxy.CodecId} has no animation entry points\");\nvar animator = NativePluginAnimator.Create(proxy, metadata, token);","typeGuard":"static bool SupportsAnimation(NativeCodecProxy p) =>\n    p.CodecApi->GetAnimationInfo != null && p.CodecApi->FreeAnimationInfo != null && p.CodecApi->DecodeAnimationFrame != null;","tryCatchPattern":"try { return NativePluginAnimator.Create(proxy, metadata, token); }\ncatch (NotSupportedException ex) when (ex.Message.Contains(\"animation entry points\"))\n{ _log.Warn($\"Codec {proxy.CodecId} has no animation ABI; treating as static.\"); return null; }","preventionTips":["Gate animator creation on a capability flag from the plugin manifest.","Do not register a static-only plugin for animated file extensions (GIF/WEBP/AVIF).","Build plugins against the current SDK so the animation entry points exist when claimed.","Fall back to SkiaAnimator for files when the plugin cannot animate."],"tags":["plugin","codec","native","animation","abi"],"backgroundTag":null,"analyzedSha":"4a3c4feceffc5a8bb5e56ba836509634aaae47a9","analyzedAt":"2026-08-13T16:58:15.523Z","schemaVersion":2},"datasetVersion":"2026-08-13T19:17:28.613Z"}