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
- Treat this as a transient cancellation: catch InvalidDataException for this specific message and skip/retry the load when the plugin reloads.
- Avoid disabling plugins while the gallery/viewer is actively decoding from them; let outstanding work drain first.
- If it happens on shutdown, ensure the host drains or cancels in-flight decodes before releasing plugin LiveTokens.
- 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
- Do not disable plugins while decodes are in flight; let outstanding work drain or cancel first.
- Link the decode CancellationToken to plugin-unload so it cancels before reaching the unloaded check.
- On shutdown, drain or cancel in-flight decodes before releasing plugin LiveTokens.
- Treat the unloaded error as transient — do not surface it as a permanent file failure.
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
- Native codec '{CodecId}' does not support static-raster deco
- IGE: Native codec '{CodecId}' threw during decode of '{fileP
- IGE: Native codec '{CodecId}' returned status {status} for '
- IGE: Native codec '{CodecId}' returned an invalid pixel buff
- IGE: Native codec '{CodecId}' returned an unsupported pixel
AI-assisted analysis of d2phap/ImageGlass@4a3c4fecef (2026-08-13).
Data as JSON: /api/errors/cb2e0b7c481f1f1b.
Report an issue: GitHub.