LorisYounger/VPet · error · InvalidDataException
fdAT found before fcTL.
Error message
fdAT found before fcTL.
What it means
APNG requires every fdAT (frame data) chunk to be preceded by an fcTL chunk defining the frame. ParseApng throws when fdAT arrives while currentFrame is null, i.e. no frame control chunk was seen first.
Solutions
- Regenerate the APNG so each fdAT sequence is preceded by fcTL (use apngasm/ffmpeg).
- Re-download the asset from the original source.
- Validate chunk ordering (acTL → fcTL → fdAT... → IEND) before load.
- Treat the file as invalid and fall back to a static image handler.
Example fix
// before // fdAT appears before any fcTL -> throw // after // rebuild with correct ordering: // ffmpeg -i in.png -plays 0 out_apng.png
Defensive patterns
Strategy: try-catch
Try / catch
try { var anim = new APNGAnimation(path); } catch (InvalidDataException ex) when (ex.Message.Contains("fdAT found before fcTL")) { Log.Error($"invalid APNG chunk order: {path}"); UseStaticFallback(path); } Prevention
- Never hand-reorder PNG chunks
- Regenerate animations from source frames instead of patching binaries
- Validate fcTL-before-fdAT ordering in asset CI
When it happens
Trigger: Loading a file containing fdAT chunks with no preceding fcTL — not a conforming APNG sequence.
Common situations: Files assembled by buggy custom encoders; chunks reordered by naive PNG manipulation; corrupted animation streams.
Understand the failure class
Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.
Related errors
- Invalid fcTL chunk.
- Invalid fdAT chunk.
- Can not find file
- No APNG frames found.
- Decode APNG frame bitmap failed.
AI-assisted analysis of LorisYounger/VPet@ffb9cc2a85 (2026-09-15).
Data as JSON: /api/errors/bc76475a8742cf1d.
Report an issue: GitHub.
Appendix: source
Thrown at VPet-Simulator.Core/Graph/APNGAnimation.cs:309
currentFrame = new ApngFrameData
{
Width = result.CanvasWidth,
Height = result.CanvasHeight,
XOffset = 0,
YOffset = 0,
DelayNum = 1,
DelayDen = 10,
DisposeOp = 0,
BlendOp = 0,
};
result.Frames.Add(currentFrame);
}
currentFrame.ImageDataChunks.Add(data);
break;
case "fdAT":
imageDataStarted = true;
if (currentFrame == null)
throw new InvalidDataException("fdAT found before fcTL.");
if (data.Length < 4)
throw new InvalidDataException("Invalid fdAT chunk.");
byte[] idatData = new byte[data.Length - 4];
Buffer.BlockCopy(data, 4, idatData, 0, idatData.Length);
currentFrame.ImageDataChunks.Add(idatData);
break;
case "IEND":
return result;
default:
if (!imageDataStarted)
{
result.SharedChunks.Add(new PngChunk { Type = type, Data = data });
}
break;
}
}
return result;View on GitHub (pinned to ffb9cc2a85)