d2phap/ImageGlass · error · InvalidDataException

IGE: Native codec '{CodecId}' was unloaded.

Error message

IGE: Native codec '{CodecId}' was unloaded.

What it means

Thrown by NativeCodecProxy.DecodeCore when _plugin.LiveToken.TryEnter() returns false. The LiveToken is the plugin-host's keepalive gate: it returns false only when the plugin is mid-teardown/unload, so a decode call would dereference memory the host is about to free. This is a defensive guard against use-after-unload, not a user error.

Source

Thrown at source/ImageGlass.Lib/Plugins/NativeCodecProxy.cs:380

        }
        return meta;
    }


    /// <summary>
    /// Invokes the plugin decode entry point and wraps the returned pixel buffer in a codec result.
    /// </summary>
    private CodecDecodeResult DecodeCore(PhotoMetadata metadata, int frameIndex, CancellationToken token)
    {
        if (_codecApi->DecodeStaticRaster == null || _codecApi->FreePixelBuffer == null)
        {
            throw new NotSupportedException($"Native codec '{CodecId}' does not support static-raster decode.");
        }

        // hold the plugin alive (see LoadMetadataCore)
        if (!_plugin.LiveToken.TryEnter())
        {
            throw new InvalidDataException($"IGE: Native codec '{CodecId}' was unloaded.");
        }

        // Register cancellation before entering native code and keep track of buffer ownership.
        var cancelHandle = PluginHostApiTable.RegisterCancellation(token);
        var filePath = metadata.FilePath;
        IGPixelBuffer buffer = default;
        var bufferOwned = false;
        var ownershipTransferred = false;

        try
        {
            IGStatus status;
            try
            {
                // Ask the plugin to decode the requested frame into its ABI buffer struct.
                fixed (char* pPath = filePath)
                {
                    var pathRef = new IGStringRef { Data = pPath, Length = filePath.Length };

View on GitHub (pinned to 4a3c4fecef)

Solutions

  1. Treat this as a transient cancellation: catch InvalidDataException for this specific message and skip/retry the load when the plugin reloads.
  2. Avoid disabling plugins while the gallery/viewer is actively decoding from them; let outstanding work drain first.
  3. If it happens on shutdown, ensure the host drains or cancels in-flight decodes before releasing plugin LiveTokens.
  4. Make sure your decode caller passes a CancellationToken that is linked to plugin-unload so it cancels cleanly instead of throwing.

Example fix

// before
if (!_plugin.LiveToken.TryEnter())
    throw new InvalidDataException($"IGE: Native codec '{CodecId}' was unloaded.");

// caller side
catch (InvalidDataException ex) when (ex.Message.Contains("was unloaded"))
{
    // plugin gone mid-decode — cancel this load, do not surface as a hard failure
    token.ThrowIfCancellationRequested();
    return CodecDecodeResult.Failed($"Plugin {CodecId} unloaded");
}
Defensive patterns

Strategy: try-catch

Validate before calling

// Before decode, confirm the plugin is still alive.
if (!proxy.Plugin.LiveToken.IsAlive) return CodecDecodeResult.Cancelled();
// (Best-effort; the live token can still release between this check and TryEnter.)

Type guard

static bool IsCodecLive(NativeCodecProxy proxy) => proxy.Plugin.LiveToken.TryEnter() switch { true => ReleaseAndReturnTrue(proxy), false => false };
// Note: TryEnter must be balanced by a release; prefer letting DecodeCore do the gate.

Try / catch

try { return proxy.DecodeStatic(metadata, frame, token); }
catch (InvalidDataException ex) when (ex.Message.Contains("was unloaded"))
{ token.ThrowIfCancellationRequested(); return CodecDecodeResult.Cancelled(); }

Prevention

When it happens

Trigger: Produced when a decode is requested for a codec whose plugin is being unloaded (user disabled it, the host is shutting down, or a hot-reload swapped the DLL). The TryEnter call at NativeCodecProxy.cs:380 returns false because the plugin's LiveToken has been released/invalidated.

Common situations: User disabled a plugin while a thumbnail/decode was in flight; the app is closing and tearing down plugins before all background work finishes; a plugin hot-reload; rapid switching between formats backed by different plugins with outstanding decode work.

Related errors


AI-assisted analysis of d2phap/ImageGlass@4a3c4fecef (2026-08-13). Data as JSON: /api/errors/cb2e0b7c481f1f1b. Report an issue: GitHub.