Skip to content

MagickImageCollection.WriteAsync(FileInfo) fails for multi‑page TIFF: "Error fetching directory count. TIFFLinkDirectory" #2081

Description

@ejosephConcord

Magick.NET version

14.16

Environment (Operating system, version and so on)

Windows 11

Description

Writing a multi‑frame MagickImageCollection to a TIFF via the async file overloads (WriteAsync(FileInfo) / WriteAsync(string)) throws:
ImageMagick.MagickCoderErrorException : Error fetching directory count. `TIFFLinkDirectory' @ error/tiff.c/TIFFErrors/564

The synchronous Write(...) works fine, and so does WriteAsync(Stream) when the stream is readable/seekable. The failure is limited to the async file path and only when the collection has more than one frame (i.e., a multi‑directory TIFF).

Steps to Reproduce

The assets/ folder contains the two inputs:

File | Frames -- | -- assets/single-page.tif | 1 assets/multi-page.tif | 56

Program.cs exposes two methods:

  • RunSinglePageAsync(...) — loads single-page.tif, tries Write and WriteAsync.
  • RunMultiPageAsync(...) — loads multi-page.tif, tries Write, WriteAsync(FileInfo) and WriteAsync(Stream).

To compare versions, change the single line in ImageMagickTest.csproj:

<PackageReference Include="Magick.NET-Q8-AnyCPU" Version="14.16.0" />  <!-- fails -->
<!-- <PackageReference Include="Magick.NET-Q8-AnyCPU" Version="14.13.1" />  works -->

Verified output

14.16.0 (libtiff 4.7.2) — FAILS

Magick.NET Q8 AnyCPU net8.0 14.16.0
== Single-page: single-page.tif ==
frames = 1
  Write(file)          OK
  WriteAsync(file)     OK
== Multi-page: multi-page.tif ==
frames = 56
  Write(file)          OK
  WriteAsync(file)     FAIL -> MagickCoderErrorException: Error fetching directory count. `TIFFLinkDirectory' @ error/tiff.c/TIFFErrors/564
  WriteAsync(stream)   OK

14.13.1 (libtiff 4.7.1) — WORKS

Magick.NET Q8 AnyCPU net8.0 14.13.1
== Single-page: single-page.tif ==
frames = 1
  Write(file)          OK
  WriteAsync(file)     OK
== Multi-page: multi-page.tif ==
frames = 56
  Write(file)          OK
  WriteAsync(file)     OK
  WriteAsync(stream)   OK

Why WriteAsync fails (root cause)

A multi-page TIFF stores each page as an IFD (Image File Directory). When writing the 2nd and later pages, libtiff calls TIFFLinkDirectory() to link the new directory into the chain.

In libtiff 4.7.2, TIFFLinkDirectory() was changed to detect IFD loops. To do that it now walks / reads back the already-written output to fetch the existing directory count. That read-back is what emits:

Error fetching directory count. `TIFFLinkDirectory' @ error/tiff.c/TIFFErrors/564

The problem is the stream the output is being written to must be readable for that read-back to succeed:

  • Write(FileInfo) (sync) → hands the filename to native libtiff, which opens the file itself with read/write access → read-back works → ✅
  • WriteAsync(Stream) with a readable stream (File.Create opens ReadWrite, or a MemoryStream) → read-back works → ✅
  • WriteAsync(FileInfo) / WriteAsync(string) → Magick.NET's async file path (WriteAsyncInternal(fileName)  WriteAsync(Stream)) opens the destination file as a write-only stream → the new read-back has no read access → ❌

Single-page TIFFs have only one IFD, so TIFFLinkDirectory() is never called — which is why they succeed on every path and every version.

Call stack (14.16.0)

ImageMagick.NativeHelper.CheckException(...)
ImageMagick.MagickImageCollection.NativeMagickImageCollection.WriteStream(...)
ImageMagick.MagickImageCollection.<>c__DisplayClass158_0.<WriteAsync>b__0()
ImageMagick.AsyncStreamWrapper.WriteAsync(...)
ImageMagick.MagickImageCollection.WriteAsync(Stream, CancellationToken)
ImageMagick.MagickImageCollection.WriteAsync(Stream, MagickFormat, CancellationToken)
ImageMagick.MagickImageCollection.WriteAsyncInternal(String fileName, Nullable<MagickFormat>, CancellationToken)
ImageMagick.MagickImageCollection.WriteAsync(FileInfo file)

Images

Image Image Image

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions