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

  1. Regenerate the APNG so each fdAT sequence is preceded by fcTL (use apngasm/ffmpeg).
  2. Re-download the asset from the original source.
  3. Validate chunk ordering (acTL → fcTL → fdAT... → IEND) before load.
  4. 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

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


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)