Add diagnostics to avifROStream, add more diagnostics
diff --git a/include/avif/internal.h b/include/avif/internal.h
index c6f23fe..9c7d44b 100644
--- a/include/avif/internal.h
+++ b/include/avif/internal.h
@@ -285,10 +285,12 @@
 {
     avifROData * raw;
     size_t offset;
+    avifDiagnostics * diag;
+    const char * diagContext;
 } avifROStream;
 
 const uint8_t * avifROStreamCurrent(avifROStream * stream);
-void avifROStreamStart(avifROStream * stream, avifROData * raw);
+void avifROStreamStart(avifROStream * stream, avifROData * raw, avifDiagnostics * diag, const char * diagContext);
 size_t avifROStreamOffset(const avifROStream * stream);
 void avifROStreamSetOffset(avifROStream * stream, size_t offset);
 
diff --git a/src/read.c b/src/read.c
index f94a9d1..a669c40 100644
--- a/src/read.c
+++ b/src/read.c
@@ -441,12 +441,12 @@
 // ---------------------------------------------------------------------------
 // Helper macros / functions
 
-#define BEGIN_STREAM(VARNAME, PTR, SIZE) \
-    avifROStream VARNAME;                \
-    avifROData VARNAME##_roData;         \
-    VARNAME##_roData.data = PTR;         \
-    VARNAME##_roData.size = SIZE;        \
-    avifROStreamStart(&VARNAME, &VARNAME##_roData)
+#define BEGIN_STREAM(VARNAME, PTR, SIZE, DIAG, CONTEXT) \
+    avifROStream VARNAME;                               \
+    avifROData VARNAME##_roData;                        \
+    VARNAME##_roData.data = PTR;                        \
+    VARNAME##_roData.size = SIZE;                       \
+    avifROStreamStart(&VARNAME, &VARNAME##_roData, DIAG, CONTEXT)
 
 // Use this to keep track of whether or not a child box that must be unique (0 or 1 present) has
 // been seen yet, when parsing a parent box. If the "seen" bit is already set for a given box when
@@ -457,7 +457,7 @@
     const uint32_t flag = 1 << whichFlag;
     if (*uniqueBoxFlags & flag) {
         // This box has already been seen. Error!
-        avifDiagnosticsPrintf(diagnostics, "Box type '%s' contains a duplicate unique box of type '%s'", parentBoxType, boxType);
+        avifDiagnosticsPrintf(diagnostics, "Box[%s] contains a duplicate unique box of type '%s'", parentBoxType, boxType);
         return AVIF_FALSE;
     }
 
@@ -725,11 +725,12 @@
     return AVIF_RESULT_OK;
 }
 
-static avifResult avifDecoderItemValidateAV1(const avifDecoderItem * item)
+static avifResult avifDecoderItemValidateAV1(const avifDecoderItem * item, avifDiagnostics * diag)
 {
     const avifProperty * av1CProp = avifPropertyArrayFind(&item->properties, "av1C");
     if (!av1CProp) {
         // An av1C box is mandatory in all valid AVIF configurations. Bail out.
+        avifDiagnosticsPrintf(diag, "Item ID %u is missing mandatory av1C property", item->id);
         return AVIF_RESULT_BMFF_PARSE_FAILED;
     }
     const uint32_t av1CDepth = avifCodecConfigurationBoxGetDepth(&av1CProp->u.av1C);
@@ -737,20 +738,27 @@
     const avifProperty * pixiProp = avifPropertyArrayFind(&item->properties, "pixi");
     if (!pixiProp) {
         // A pixi box is mandatory in all valid AVIF configurations. Bail out.
+        avifDiagnosticsPrintf(diag, "Item ID %u is missing mandatory pixi property", item->id);
         return AVIF_RESULT_BMFF_PARSE_FAILED;
     }
 
     for (uint8_t i = 0; i < pixiProp->u.pixi.planeCount; ++i) {
         if (pixiProp->u.pixi.planeDepths[i] != av1CDepth) {
             // pixi depth must match av1C depth
+            avifDiagnosticsPrintf(diag,
+                                  "Item ID %u depth specified by pixi property [%u] does not match av1C property depth [%u]",
+                                  item->id,
+                                  pixiProp->u.pixi.planeDepths[i],
+                                  av1CDepth);
             return AVIF_RESULT_BMFF_PARSE_FAILED;
         }
     }
     return AVIF_RESULT_OK;
 }
 
-static avifResult avifDecoderItemRead(avifDecoderItem * item, avifIO * io, avifROData * outData, size_t partialByteCount)
+static avifResult avifDecoderItemRead(avifDecoderItem * item, avifIO * io, avifROData * outData, size_t partialByteCount, avifDiagnostics * diag)
 {
+    (void)diag;
     if (item->mergedExtents.data && !item->partialMergedExtents) {
         // Multiple extents have already been concatenated for this item, just return it
         memcpy(outData, &item->mergedExtents, sizeof(avifROData));
@@ -758,6 +766,7 @@
     }
 
     if (item->extents.count == 0) {
+        avifDiagnosticsPrintf(diag, "Item ID %u has zero extents", item->id);
         return AVIF_RESULT_TRUNCATED_DATA;
     }
 
@@ -776,14 +785,15 @@
 
         if (idatBuffer == NULL) {
             // no associated idat box was found in the meta box, bail out
+            avifDiagnosticsPrintf(diag, "Item ID %u is stored in an idat, but no associated idat box was found", item->id);
             return AVIF_RESULT_NO_CONTENT;
         }
     }
 
     // Merge extents into a single contiguous buffer
     if ((io->sizeHint > 0) && (item->size > io->sizeHint)) {
-        // Sanity check: somehow the sum of extents for this item exceeds the entire file or idat
-        // size!
+        // Sanity check: somehow the sum of extents exceeds the entire file or idat size!
+        avifDiagnosticsPrintf(diag, "Item ID %u reported size failed size hint sanity check. Truncated data?", item->id);
         return AVIF_RESULT_TRUNCATED_DATA;
     }
 
@@ -818,9 +828,11 @@
         avifROData offsetBuffer;
         if (idatBuffer) {
             if (extent->offset > idatBuffer->size) {
+                avifDiagnosticsPrintf(diag, "Item ID %u has impossible extent offset in idat buffer", item->id);
                 return AVIF_RESULT_BMFF_PARSE_FAILED;
             }
             if (extent->size > idatBuffer->size - extent->offset) {
+                avifDiagnosticsPrintf(diag, "Item ID %u has impossible extent size in idat buffer", item->id);
                 return AVIF_RESULT_BMFF_PARSE_FAILED;
             }
             offsetBuffer.data = idatBuffer->data + extent->offset;
@@ -829,6 +841,7 @@
             // construction_method: file(0)
 
             if ((io->sizeHint > 0) && (extent->offset > io->sizeHint)) {
+                avifDiagnosticsPrintf(diag, "Item ID %u extent offset failed size hint sanity check. Truncated data?", item->id);
                 return AVIF_RESULT_BMFF_PARSE_FAILED;
             }
             avifResult readResult = io->read(io, 0, extent->offset, bytesToRead, &offsetBuffer);
@@ -836,6 +849,10 @@
                 return readResult;
             }
             if (bytesToRead != offsetBuffer.size) {
+                avifDiagnosticsPrintf(diag,
+                                      "Item ID %u tried to read %zu bytes, but only received %zu bytes",
+                                      bytesToRead,
+                                      offsetBuffer.size);
                 return AVIF_RESULT_TRUNCATED_DATA;
             }
         }
@@ -858,6 +875,7 @@
     }
     if (remainingBytes != 0) {
         // This should be impossible?
+        avifDiagnosticsPrintf(diag, "Item ID %u has %zu unexpected trailing bytes", item->id, remainingBytes);
         return AVIF_RESULT_TRUNCATED_DATA;
     }
 
@@ -1140,13 +1158,13 @@
 
         if (!decoder->ignoreExif && !memcmp(item->type, "Exif", 4)) {
             avifROData exifContents;
-            avifResult readResult = avifDecoderItemRead(item, decoder->io, &exifContents, 0);
+            avifResult readResult = avifDecoderItemRead(item, decoder->io, &exifContents, 0, &decoder->diag);
             if (readResult != AVIF_RESULT_OK) {
                 return readResult;
             }
 
             // Advance past Annex A.2.1's header
-            BEGIN_STREAM(exifBoxStream, exifContents.data, exifContents.size);
+            BEGIN_STREAM(exifBoxStream, exifContents.data, exifContents.size, &decoder->diag, "Exif header");
             uint32_t exifTiffHeaderOffset;
             CHECKERR(avifROStreamReadU32(&exifBoxStream, &exifTiffHeaderOffset), AVIF_RESULT_BMFF_PARSE_FAILED); // unsigned int(32) exif_tiff_header_offset;
 
@@ -1154,7 +1172,7 @@
         } else if (!decoder->ignoreXMP && !memcmp(item->type, "mime", 4) &&
                    !memcmp(item->contentType.contentType, xmpContentType, xmpContentTypeSize)) {
             avifROData xmpContents;
-            avifResult readResult = avifDecoderItemRead(item, decoder->io, &xmpContents, 0);
+            avifResult readResult = avifDecoderItemRead(item, decoder->io, &xmpContents, 0, &decoder->diag);
             if (readResult != AVIF_RESULT_OK) {
                 return readResult;
             }
@@ -1178,21 +1196,21 @@
 
 static avifBool avifParseHandlerBox(const uint8_t * raw, size_t rawLen, avifDiagnostics * diag)
 {
-    BEGIN_STREAM(s, raw, rawLen);
+    BEGIN_STREAM(s, raw, rawLen, diag, "Box[hdlr]");
 
     CHECK(avifROStreamReadAndEnforceVersion(&s, 0));
 
     uint32_t predefined;
     CHECK(avifROStreamReadU32(&s, &predefined)); // unsigned int(32) pre_defined = 0;
     if (predefined != 0) {
-        avifDiagnosticsPrintf(diag, "Box type 'hdlr' contains a pre_defined value that is nonzero");
+        avifDiagnosticsPrintf(diag, "Box[hdlr] contains a pre_defined value that is nonzero");
         return AVIF_FALSE;
     }
 
     uint8_t handlerType[4];
     CHECK(avifROStreamRead(&s, handlerType, 4)); // unsigned int(32) handler_type;
     if (memcmp(handlerType, "pict", 4) != 0) {
-        avifDiagnosticsPrintf(diag, "Box type 'hdlr' handler_type is not 'pict'");
+        avifDiagnosticsPrintf(diag, "Box[hdlr] handler_type is not 'pict'");
         return AVIF_FALSE;
     }
 
@@ -1208,12 +1226,12 @@
 
 static avifBool avifParseItemLocationBox(avifMeta * meta, const uint8_t * raw, size_t rawLen, avifDiagnostics * diag)
 {
-    BEGIN_STREAM(s, raw, rawLen);
+    BEGIN_STREAM(s, raw, rawLen, diag, "Box[iloc]");
 
     uint8_t version;
     CHECK(avifROStreamReadVersionAndFlags(&s, &version, NULL));
     if (version > 2) {
-        avifDiagnosticsPrintf(diag, "Box type 'iloc' has an unsupported version [%u]", version);
+        avifDiagnosticsPrintf(diag, "Box[iloc] has an unsupported version [%u]", version);
         return AVIF_FALSE;
     }
 
@@ -1230,7 +1248,7 @@
         indexSize = baseOffsetSizeAndIndexSize & 0xf; // unsigned int(4) index_size;
         if (indexSize != 0) {
             // extent_index unsupported
-            avifDiagnosticsPrintf(diag, "Box type 'iloc' has an unsupported extent_index");
+            avifDiagnosticsPrintf(diag, "Box[iloc] has an unsupported extent_index");
             return AVIF_FALSE;
         }
     }
@@ -1261,7 +1279,7 @@
             constructionMethod = constructionMethod & 0xf;
             if ((constructionMethod != 0 /* file */) && (constructionMethod != 1 /* idat */)) {
                 // construction method item(2) unsupported
-                avifDiagnosticsPrintf(diag, "Box type 'iloc' has an unsupported construction method [%u]", constructionMethod);
+                avifDiagnosticsPrintf(diag, "Box[iloc] has an unsupported construction method [%u]", constructionMethod);
                 return AVIF_FALSE;
             }
             if (constructionMethod == 1) {
@@ -1271,7 +1289,7 @@
 
         avifDecoderItem * item = avifMetaFindItem(meta, itemID);
         if (!item) {
-            avifDiagnosticsPrintf(diag, "Box type 'iloc' has an invalid item ID [%u]", itemID);
+            avifDiagnosticsPrintf(diag, "Box[iloc] has an invalid item ID [%u]", itemID);
             return AVIF_FALSE;
         }
         if (item->extents.count > 0) {
@@ -1330,7 +1348,7 @@
 
 static avifBool avifParseImageGridBox(avifImageGrid * grid, const uint8_t * raw, size_t rawLen, avifDiagnostics * diag)
 {
-    BEGIN_STREAM(s, raw, rawLen);
+    BEGIN_STREAM(s, raw, rawLen, diag, "Box[grid]");
 
     uint8_t version, flags;
     CHECK(avifROStreamRead(&s, &version, 1)); // unsigned int(8) version = 0;
@@ -1367,9 +1385,9 @@
     return avifROStreamRemainingBytes(&s) == 0;
 }
 
-static avifBool avifParseImageSpatialExtentsProperty(avifProperty * prop, const uint8_t * raw, size_t rawLen)
+static avifBool avifParseImageSpatialExtentsProperty(avifProperty * prop, const uint8_t * raw, size_t rawLen, avifDiagnostics * diag)
 {
-    BEGIN_STREAM(s, raw, rawLen);
+    BEGIN_STREAM(s, raw, rawLen, diag, "Box[ispe]");
     CHECK(avifROStreamReadAndEnforceVersion(&s, 0));
 
     avifImageSpatialExtents * ispe = &prop->u.ispe;
@@ -1378,18 +1396,18 @@
     return AVIF_TRUE;
 }
 
-static avifBool avifParseAuxiliaryTypeProperty(avifProperty * prop, const uint8_t * raw, size_t rawLen)
+static avifBool avifParseAuxiliaryTypeProperty(avifProperty * prop, const uint8_t * raw, size_t rawLen, avifDiagnostics * diag)
 {
-    BEGIN_STREAM(s, raw, rawLen);
+    BEGIN_STREAM(s, raw, rawLen, diag, "Box[auxC]");
     CHECK(avifROStreamReadAndEnforceVersion(&s, 0));
 
     CHECK(avifROStreamReadString(&s, prop->u.auxC.auxType, AUXTYPE_SIZE));
     return AVIF_TRUE;
 }
 
-static avifBool avifParseColourInformationBox(avifProperty * prop, const uint8_t * raw, size_t rawLen)
+static avifBool avifParseColourInformationBox(avifProperty * prop, const uint8_t * raw, size_t rawLen, avifDiagnostics * diag)
 {
-    BEGIN_STREAM(s, raw, rawLen);
+    BEGIN_STREAM(s, raw, rawLen, diag, "Box[colr]");
 
     avifColourInformationBox * colr = &prop->u.colr;
     colr->hasICC = AVIF_FALSE;
@@ -1417,7 +1435,7 @@
 
 static avifBool avifParseAV1CodecConfigurationBox(const uint8_t * raw, size_t rawLen, avifCodecConfigurationBox * av1C, avifDiagnostics * diag)
 {
-    BEGIN_STREAM(s, raw, rawLen);
+    BEGIN_STREAM(s, raw, rawLen, diag, "Box[av1C]");
 
     uint8_t markerAndVersion = 0;
     CHECK(avifROStreamRead(&s, &markerAndVersion, 1));
@@ -1449,9 +1467,9 @@
     return avifParseAV1CodecConfigurationBox(raw, rawLen, &prop->u.av1C, diag);
 }
 
-static avifBool avifParsePixelAspectRatioBoxProperty(avifProperty * prop, const uint8_t * raw, size_t rawLen)
+static avifBool avifParsePixelAspectRatioBoxProperty(avifProperty * prop, const uint8_t * raw, size_t rawLen, avifDiagnostics * diag)
 {
-    BEGIN_STREAM(s, raw, rawLen);
+    BEGIN_STREAM(s, raw, rawLen, diag, "Box[pasp]");
 
     avifPixelAspectRatioBox * pasp = &prop->u.pasp;
     CHECK(avifROStreamReadU32(&s, &pasp->hSpacing)); // unsigned int(32) hSpacing;
@@ -1459,9 +1477,9 @@
     return AVIF_TRUE;
 }
 
-static avifBool avifParseCleanApertureBoxProperty(avifProperty * prop, const uint8_t * raw, size_t rawLen)
+static avifBool avifParseCleanApertureBoxProperty(avifProperty * prop, const uint8_t * raw, size_t rawLen, avifDiagnostics * diag)
 {
-    BEGIN_STREAM(s, raw, rawLen);
+    BEGIN_STREAM(s, raw, rawLen, diag, "Box[clap]");
 
     avifCleanApertureBox * clap = &prop->u.clap;
     CHECK(avifROStreamReadU32(&s, &clap->widthN));    // unsigned int(32) cleanApertureWidthN;
@@ -1477,13 +1495,13 @@
 
 static avifBool avifParseImageRotationProperty(avifProperty * prop, const uint8_t * raw, size_t rawLen, avifDiagnostics * diag)
 {
-    BEGIN_STREAM(s, raw, rawLen);
+    BEGIN_STREAM(s, raw, rawLen, diag, "Box[irot]");
 
     avifImageRotation * irot = &prop->u.irot;
     CHECK(avifROStreamRead(&s, &irot->angle, 1)); // unsigned int (6) reserved = 0; unsigned int (2) angle;
     if ((irot->angle & 0xfc) != 0) {
         // reserved bits must be 0
-        avifDiagnosticsPrintf(diag, "Box type 'irot' contains nonzero reserved bits [%u]", irot->angle);
+        avifDiagnosticsPrintf(diag, "Box[irot] contains nonzero reserved bits [%u]", irot->angle);
         return AVIF_FALSE;
     }
     return AVIF_TRUE;
@@ -1491,13 +1509,13 @@
 
 static avifBool avifParseImageMirrorProperty(avifProperty * prop, const uint8_t * raw, size_t rawLen, avifDiagnostics * diag)
 {
-    BEGIN_STREAM(s, raw, rawLen);
+    BEGIN_STREAM(s, raw, rawLen, diag, "Box[imir]");
 
     avifImageMirror * imir = &prop->u.imir;
     CHECK(avifROStreamRead(&s, &imir->axis, 1)); // unsigned int (7) reserved = 0; unsigned int (1) axis;
     if ((imir->axis & 0xfe) != 0) {
         // reserved bits must be 0
-        avifDiagnosticsPrintf(diag, "Box type 'imir' contains nonzero reserved bits [%u]", imir->axis);
+        avifDiagnosticsPrintf(diag, "Box[imir] contains nonzero reserved bits [%u]", imir->axis);
         return AVIF_FALSE;
     }
     return AVIF_TRUE;
@@ -1505,13 +1523,13 @@
 
 static avifBool avifParsePixelInformationProperty(avifProperty * prop, const uint8_t * raw, size_t rawLen, avifDiagnostics * diag)
 {
-    BEGIN_STREAM(s, raw, rawLen);
+    BEGIN_STREAM(s, raw, rawLen, diag, "Box[pixi]");
     CHECK(avifROStreamReadAndEnforceVersion(&s, 0));
 
     avifPixelInformationProperty * pixi = &prop->u.pixi;
     CHECK(avifROStreamRead(&s, &pixi->planeCount, 1)); // unsigned int (8) num_channels;
     if (pixi->planeCount > MAX_PIXI_PLANE_DEPTHS) {
-        avifDiagnosticsPrintf(diag, "Box type 'pixi' contains unsupported plane count [%u]", pixi->planeCount);
+        avifDiagnosticsPrintf(diag, "Box[pixi] contains unsupported plane count [%u]", pixi->planeCount);
         return AVIF_FALSE;
     }
     for (uint8_t i = 0; i < pixi->planeCount; ++i) {
@@ -1522,7 +1540,7 @@
 
 static avifBool avifParseItemPropertyContainerBox(avifPropertyArray * properties, const uint8_t * raw, size_t rawLen, avifDiagnostics * diag)
 {
-    BEGIN_STREAM(s, raw, rawLen);
+    BEGIN_STREAM(s, raw, rawLen, diag, "Box[iprp]");
 
     while (avifROStreamHasBytesLeft(&s, 1)) {
         avifBoxHeader header;
@@ -1532,17 +1550,17 @@
         avifProperty * prop = &properties->prop[propertyIndex];
         memcpy(prop->type, header.type, 4);
         if (!memcmp(header.type, "ispe", 4)) {
-            CHECK(avifParseImageSpatialExtentsProperty(prop, avifROStreamCurrent(&s), header.size));
+            CHECK(avifParseImageSpatialExtentsProperty(prop, avifROStreamCurrent(&s), header.size, diag));
         } else if (!memcmp(header.type, "auxC", 4)) {
-            CHECK(avifParseAuxiliaryTypeProperty(prop, avifROStreamCurrent(&s), header.size));
+            CHECK(avifParseAuxiliaryTypeProperty(prop, avifROStreamCurrent(&s), header.size, diag));
         } else if (!memcmp(header.type, "colr", 4)) {
-            CHECK(avifParseColourInformationBox(prop, avifROStreamCurrent(&s), header.size));
+            CHECK(avifParseColourInformationBox(prop, avifROStreamCurrent(&s), header.size, diag));
         } else if (!memcmp(header.type, "av1C", 4)) {
             CHECK(avifParseAV1CodecConfigurationBoxProperty(prop, avifROStreamCurrent(&s), header.size, diag));
         } else if (!memcmp(header.type, "pasp", 4)) {
-            CHECK(avifParsePixelAspectRatioBoxProperty(prop, avifROStreamCurrent(&s), header.size));
+            CHECK(avifParsePixelAspectRatioBoxProperty(prop, avifROStreamCurrent(&s), header.size, diag));
         } else if (!memcmp(header.type, "clap", 4)) {
-            CHECK(avifParseCleanApertureBoxProperty(prop, avifROStreamCurrent(&s), header.size));
+            CHECK(avifParseCleanApertureBoxProperty(prop, avifROStreamCurrent(&s), header.size, diag));
         } else if (!memcmp(header.type, "irot", 4)) {
             CHECK(avifParseImageRotationProperty(prop, avifROStreamCurrent(&s), header.size, diag));
         } else if (!memcmp(header.type, "imir", 4)) {
@@ -1561,7 +1579,7 @@
     // NOTE: If this function ever adds support for versions other than [0,1] or flags other than
     //       [0,1], please increase the value of MAX_IPMA_VERSION_AND_FLAGS_SEEN accordingly.
 
-    BEGIN_STREAM(s, raw, rawLen);
+    BEGIN_STREAM(s, raw, rawLen, diag, "Box[ipma]");
 
     uint8_t version;
     uint32_t flags;
@@ -1585,18 +1603,18 @@
             CHECK(avifROStreamReadU32(&s, &itemID));
         }
         if (itemID <= prevItemID) {
-            avifDiagnosticsPrintf(diag, "Box type 'ipma' item IDs are not ordered by increasing ID");
+            avifDiagnosticsPrintf(diag, "Box[ipma] item IDs are not ordered by increasing ID");
             return AVIF_FALSE;
         }
         prevItemID = itemID;
 
         avifDecoderItem * item = avifMetaFindItem(meta, itemID);
         if (!item) {
-            avifDiagnosticsPrintf(diag, "Box type 'ipma' has an invalid item ID [%u]", itemID);
+            avifDiagnosticsPrintf(diag, "Box[ipma] has an invalid item ID [%u]", itemID);
             return AVIF_FALSE;
         }
         if (item->ipmaSeen) {
-            avifDiagnosticsPrintf(diag, "Duplicate box type 'ipma' for item ID [%u]", itemID);
+            avifDiagnosticsPrintf(diag, "Duplicate Box[ipma] for item ID [%u]", itemID);
             return AVIF_FALSE;
         }
         item->ipmaSeen = AVIF_TRUE;
@@ -1625,7 +1643,7 @@
 
             if (propertyIndex >= meta->properties.count) {
                 avifDiagnosticsPrintf(diag,
-                                      "Box type 'ipma' for item ID [%u] contains an illegal property index [%u] (out of [%u] properties)",
+                                      "Box[ipma] for item ID [%u] contains an illegal property index [%u] (out of [%u] properties)",
                                       itemID,
                                       propertyIndex,
                                       meta->properties.count);
@@ -1664,11 +1682,11 @@
 {
     if (meta->primaryItemID > 0) {
         // Illegal to have multiple pitm boxes, bail out
-        avifDiagnosticsPrintf(diag, "Multiple boxes of unique box type 'pitm' found");
+        avifDiagnosticsPrintf(diag, "Multiple boxes of unique Box[pitm] found");
         return AVIF_FALSE;
     }
 
-    BEGIN_STREAM(s, raw, rawLen);
+    BEGIN_STREAM(s, raw, rawLen, diag, "Box[pitm]");
 
     uint8_t version;
     CHECK(avifROStreamReadVersionAndFlags(&s, &version, NULL));
@@ -1702,7 +1720,7 @@
 
 static avifBool avifParseItemPropertiesBox(avifMeta * meta, const uint8_t * raw, size_t rawLen, avifDiagnostics * diag)
 {
-    BEGIN_STREAM(s, raw, rawLen);
+    BEGIN_STREAM(s, raw, rawLen, diag, "Box[iprp]");
 
     avifBoxHeader ipcoHeader;
     CHECK(avifROStreamReadBoxHeader(&s, &ipcoHeader));
@@ -1730,7 +1748,7 @@
                     // HEIF (ISO 23008-12:2017) 9.3.1 - There shall be at most one
                     // ItemPropertyAssociation box with a given pair of values of version and
                     // flags.
-                    avifDiagnosticsPrintf(diag, "Multiple box type 'ipma' with a given pair of values of version and flags. See HEIF (ISO 23008-12:2017) 9.3.1");
+                    avifDiagnosticsPrintf(diag, "Multiple Box[ipma] with a given pair of values of version and flags. See HEIF (ISO 23008-12:2017) 9.3.1");
                     return AVIF_FALSE;
                 }
             }
@@ -1742,7 +1760,7 @@
             ++versionAndFlagsSeenCount;
         } else {
             // These must all be type ipma
-            avifDiagnosticsPrintf(diag, "Box type 'iprp' contains a box that isn't type 'ipma'");
+            avifDiagnosticsPrintf(diag, "Box[iprp] contains a box that isn't type 'ipma'");
             return AVIF_FALSE;
         }
 
@@ -1753,7 +1771,7 @@
 
 static avifBool avifParseItemInfoEntry(avifMeta * meta, const uint8_t * raw, size_t rawLen, avifDiagnostics * diag)
 {
-    BEGIN_STREAM(s, raw, rawLen);
+    BEGIN_STREAM(s, raw, rawLen, diag, "Box[infe]");
 
     CHECK(avifROStreamReadAndEnforceVersion(&s, 2)); // TODO: support version > 2? 2+ is required for item_type
 
@@ -1774,7 +1792,7 @@
 
     avifDecoderItem * item = avifMetaFindItem(meta, itemID);
     if (!item) {
-        avifDiagnosticsPrintf(diag, "Box type 'infe' has an invalid item ID [%u]", itemID);
+        avifDiagnosticsPrintf(diag, "Box[infe] has an invalid item ID [%u]", itemID);
         return AVIF_FALSE;
     }
 
@@ -1785,7 +1803,7 @@
 
 static avifBool avifParseItemInfoBox(avifMeta * meta, const uint8_t * raw, size_t rawLen, avifDiagnostics * diag)
 {
-    BEGIN_STREAM(s, raw, rawLen);
+    BEGIN_STREAM(s, raw, rawLen, diag, "Box[iinf]");
 
     uint8_t version;
     CHECK(avifROStreamReadVersionAndFlags(&s, &version, NULL));
@@ -1807,7 +1825,8 @@
         if (!memcmp(infeHeader.type, "infe", 4)) {
             CHECK(avifParseItemInfoEntry(meta, avifROStreamCurrent(&s), infeHeader.size, diag));
         } else {
-            // These must all be type ipma
+            // These must all be type infe
+            avifDiagnosticsPrintf(diag, "Box[iinf] contains a box that isn't type 'infe'");
             return AVIF_FALSE;
         }
 
@@ -1819,7 +1838,7 @@
 
 static avifBool avifParseItemReferenceBox(avifMeta * meta, const uint8_t * raw, size_t rawLen, avifDiagnostics * diag)
 {
-    BEGIN_STREAM(s, raw, rawLen);
+    BEGIN_STREAM(s, raw, rawLen, diag, "Box[iref]");
 
     uint8_t version;
     CHECK(avifROStreamReadVersionAndFlags(&s, &version, NULL));
@@ -1860,7 +1879,7 @@
             if (fromID && toID) {
                 avifDecoderItem * item = avifMetaFindItem(meta, fromID);
                 if (!item) {
-                    avifDiagnosticsPrintf(diag, "Box type 'iref' has an invalid item ID [%u]", fromID);
+                    avifDiagnosticsPrintf(diag, "Box[iref] has an invalid item ID [%u]", fromID);
                     return AVIF_FALSE;
                 }
 
@@ -1874,7 +1893,7 @@
                     // derived images refer in the opposite direction
                     avifDecoderItem * dimg = avifMetaFindItem(meta, toID);
                     if (!dimg) {
-                        avifDiagnosticsPrintf(diag, "Box type 'iref' has an invalid item ID dimg ref [%u]", toID);
+                        avifDiagnosticsPrintf(diag, "Box[iref] has an invalid item ID dimg ref [%u]", toID);
                         return AVIF_FALSE;
                     }
 
@@ -1891,7 +1910,7 @@
 
 static avifBool avifParseMetaBox(avifMeta * meta, const uint8_t * raw, size_t rawLen, avifDiagnostics * diag)
 {
-    BEGIN_STREAM(s, raw, rawLen);
+    BEGIN_STREAM(s, raw, rawLen, diag, "Box[meta]");
 
     CHECK(avifROStreamReadAndEnforceVersion(&s, 0));
 
@@ -1943,7 +1962,7 @@
 
 static avifBool avifParseTrackHeaderBox(avifTrack * track, const uint8_t * raw, size_t rawLen, avifDiagnostics * diag)
 {
-    BEGIN_STREAM(s, raw, rawLen);
+    BEGIN_STREAM(s, raw, rawLen, diag, "Box[tkhd]");
 
     uint8_t version;
     CHECK(avifROStreamReadVersionAndFlags(&s, &version, NULL));
@@ -1964,7 +1983,7 @@
         CHECK(avifROStreamReadU32(&s, &ignored32)); // unsigned int(32) duration;
     } else {
         // Unsupported version
-        avifDiagnosticsPrintf(diag, "Box type 'tkhd' has an unsupported version [%u]", version);
+        avifDiagnosticsPrintf(diag, "Box[tkhd] has an unsupported version [%u]", version);
         return AVIF_FALSE;
     }
 
@@ -1992,7 +2011,7 @@
 
 static avifBool avifParseMediaHeaderBox(avifTrack * track, const uint8_t * raw, size_t rawLen, avifDiagnostics * diag)
 {
-    BEGIN_STREAM(s, raw, rawLen);
+    BEGIN_STREAM(s, raw, rawLen, diag, "Box[mdhd]");
 
     uint8_t version;
     CHECK(avifROStreamReadVersionAndFlags(&s, &version, NULL));
@@ -2013,7 +2032,7 @@
         track->mediaDuration = (uint64_t)mediaDuration32;
     } else {
         // Unsupported version
-        avifDiagnosticsPrintf(diag, "Box type 'mdhd' has an unsupported version [%u]", version);
+        avifDiagnosticsPrintf(diag, "Box[mdhd] has an unsupported version [%u]", version);
         return AVIF_FALSE;
     }
 
@@ -2021,9 +2040,9 @@
     return AVIF_TRUE;
 }
 
-static avifBool avifParseChunkOffsetBox(avifSampleTable * sampleTable, avifBool largeOffsets, const uint8_t * raw, size_t rawLen)
+static avifBool avifParseChunkOffsetBox(avifSampleTable * sampleTable, avifBool largeOffsets, const uint8_t * raw, size_t rawLen, avifDiagnostics * diag)
 {
-    BEGIN_STREAM(s, raw, rawLen);
+    BEGIN_STREAM(s, raw, rawLen, diag, "Box[stco]");
 
     CHECK(avifROStreamReadAndEnforceVersion(&s, 0));
 
@@ -2047,7 +2066,7 @@
 
 static avifBool avifParseSampleToChunkBox(avifSampleTable * sampleTable, const uint8_t * raw, size_t rawLen, avifDiagnostics * diag)
 {
-    BEGIN_STREAM(s, raw, rawLen);
+    BEGIN_STREAM(s, raw, rawLen, diag, "Box[stsc]");
 
     CHECK(avifROStreamReadAndEnforceVersion(&s, 0));
 
@@ -2062,12 +2081,12 @@
         // The first_chunk fields should start with 1 and be strictly increasing.
         if (i == 0) {
             if (sampleToChunk->firstChunk != 1) {
-                avifDiagnosticsPrintf(diag, "Box type 'stsc' does not begin with chunk 1 [%u]", sampleToChunk->firstChunk);
+                avifDiagnosticsPrintf(diag, "Box[stsc] does not begin with chunk 1 [%u]", sampleToChunk->firstChunk);
                 return AVIF_FALSE;
             }
         } else {
             if (sampleToChunk->firstChunk <= prevFirstChunk) {
-                avifDiagnosticsPrintf(diag, "Box type 'stsc' chunks are not strictly increasing");
+                avifDiagnosticsPrintf(diag, "Box[stsc] chunks are not strictly increasing");
                 return AVIF_FALSE;
             }
         }
@@ -2076,9 +2095,9 @@
     return AVIF_TRUE;
 }
 
-static avifBool avifParseSampleSizeBox(avifSampleTable * sampleTable, const uint8_t * raw, size_t rawLen)
+static avifBool avifParseSampleSizeBox(avifSampleTable * sampleTable, const uint8_t * raw, size_t rawLen, avifDiagnostics * diag)
 {
-    BEGIN_STREAM(s, raw, rawLen);
+    BEGIN_STREAM(s, raw, rawLen, diag, "Box[stsz]");
 
     CHECK(avifROStreamReadAndEnforceVersion(&s, 0));
 
@@ -2097,9 +2116,9 @@
     return AVIF_TRUE;
 }
 
-static avifBool avifParseSyncSampleBox(avifSampleTable * sampleTable, const uint8_t * raw, size_t rawLen)
+static avifBool avifParseSyncSampleBox(avifSampleTable * sampleTable, const uint8_t * raw, size_t rawLen, avifDiagnostics * diag)
 {
-    BEGIN_STREAM(s, raw, rawLen);
+    BEGIN_STREAM(s, raw, rawLen, diag, "Box[stss]");
 
     CHECK(avifROStreamReadAndEnforceVersion(&s, 0));
 
@@ -2115,9 +2134,9 @@
     return AVIF_TRUE;
 }
 
-static avifBool avifParseTimeToSampleBox(avifSampleTable * sampleTable, const uint8_t * raw, size_t rawLen)
+static avifBool avifParseTimeToSampleBox(avifSampleTable * sampleTable, const uint8_t * raw, size_t rawLen, avifDiagnostics * diag)
 {
-    BEGIN_STREAM(s, raw, rawLen);
+    BEGIN_STREAM(s, raw, rawLen, diag, "Box[stts]");
 
     CHECK(avifROStreamReadAndEnforceVersion(&s, 0));
 
@@ -2134,7 +2153,7 @@
 
 static avifBool avifParseSampleDescriptionBox(avifSampleTable * sampleTable, const uint8_t * raw, size_t rawLen, avifDiagnostics * diag)
 {
-    BEGIN_STREAM(s, raw, rawLen);
+    BEGIN_STREAM(s, raw, rawLen, diag, "Box[stsd]");
 
     CHECK(avifROStreamReadAndEnforceVersion(&s, 0));
 
@@ -2169,24 +2188,24 @@
     }
     track->sampleTable = avifSampleTableCreate();
 
-    BEGIN_STREAM(s, raw, rawLen);
+    BEGIN_STREAM(s, raw, rawLen, diag, "Box[stbl]");
 
     while (avifROStreamHasBytesLeft(&s, 1)) {
         avifBoxHeader header;
         CHECK(avifROStreamReadBoxHeader(&s, &header));
 
         if (!memcmp(header.type, "stco", 4)) {
-            CHECK(avifParseChunkOffsetBox(track->sampleTable, AVIF_FALSE, avifROStreamCurrent(&s), header.size));
+            CHECK(avifParseChunkOffsetBox(track->sampleTable, AVIF_FALSE, avifROStreamCurrent(&s), header.size, diag));
         } else if (!memcmp(header.type, "co64", 4)) {
-            CHECK(avifParseChunkOffsetBox(track->sampleTable, AVIF_TRUE, avifROStreamCurrent(&s), header.size));
+            CHECK(avifParseChunkOffsetBox(track->sampleTable, AVIF_TRUE, avifROStreamCurrent(&s), header.size, diag));
         } else if (!memcmp(header.type, "stsc", 4)) {
             CHECK(avifParseSampleToChunkBox(track->sampleTable, avifROStreamCurrent(&s), header.size, diag));
         } else if (!memcmp(header.type, "stsz", 4)) {
-            CHECK(avifParseSampleSizeBox(track->sampleTable, avifROStreamCurrent(&s), header.size));
+            CHECK(avifParseSampleSizeBox(track->sampleTable, avifROStreamCurrent(&s), header.size, diag));
         } else if (!memcmp(header.type, "stss", 4)) {
-            CHECK(avifParseSyncSampleBox(track->sampleTable, avifROStreamCurrent(&s), header.size));
+            CHECK(avifParseSyncSampleBox(track->sampleTable, avifROStreamCurrent(&s), header.size, diag));
         } else if (!memcmp(header.type, "stts", 4)) {
-            CHECK(avifParseTimeToSampleBox(track->sampleTable, avifROStreamCurrent(&s), header.size));
+            CHECK(avifParseTimeToSampleBox(track->sampleTable, avifROStreamCurrent(&s), header.size, diag));
         } else if (!memcmp(header.type, "stsd", 4)) {
             CHECK(avifParseSampleDescriptionBox(track->sampleTable, avifROStreamCurrent(&s), header.size, diag));
         }
@@ -2198,7 +2217,7 @@
 
 static avifBool avifParseMediaInformationBox(avifTrack * track, const uint8_t * raw, size_t rawLen, avifDiagnostics * diag)
 {
-    BEGIN_STREAM(s, raw, rawLen);
+    BEGIN_STREAM(s, raw, rawLen, diag, "Box[minf]");
 
     while (avifROStreamHasBytesLeft(&s, 1)) {
         avifBoxHeader header;
@@ -2215,7 +2234,7 @@
 
 static avifBool avifParseMediaBox(avifTrack * track, const uint8_t * raw, size_t rawLen, avifDiagnostics * diag)
 {
-    BEGIN_STREAM(s, raw, rawLen);
+    BEGIN_STREAM(s, raw, rawLen, diag, "Box[mdia]");
 
     while (avifROStreamHasBytesLeft(&s, 1)) {
         avifBoxHeader header;
@@ -2232,9 +2251,9 @@
     return AVIF_TRUE;
 }
 
-static avifBool avifTrackReferenceBox(avifTrack * track, const uint8_t * raw, size_t rawLen)
+static avifBool avifTrackReferenceBox(avifTrack * track, const uint8_t * raw, size_t rawLen, avifDiagnostics * diag)
 {
-    BEGIN_STREAM(s, raw, rawLen);
+    BEGIN_STREAM(s, raw, rawLen, diag, "Box[tref]");
 
     while (avifROStreamHasBytesLeft(&s, 1)) {
         avifBoxHeader header;
@@ -2257,9 +2276,9 @@
     return AVIF_TRUE;
 }
 
-static avifBool avifParseTrackBox(avifDecoderData * data, const uint8_t * raw, size_t rawLen)
+static avifBool avifParseTrackBox(avifDecoderData * data, const uint8_t * raw, size_t rawLen, avifDiagnostics * diag)
 {
-    BEGIN_STREAM(s, raw, rawLen);
+    BEGIN_STREAM(s, raw, rawLen, diag, "Box[trak]");
 
     avifTrack * track = avifDecoderDataCreateTrack(data);
 
@@ -2274,7 +2293,7 @@
         } else if (!memcmp(header.type, "mdia", 4)) {
             CHECK(avifParseMediaBox(track, avifROStreamCurrent(&s), header.size, data->diag));
         } else if (!memcmp(header.type, "tref", 4)) {
-            CHECK(avifTrackReferenceBox(track, avifROStreamCurrent(&s), header.size));
+            CHECK(avifTrackReferenceBox(track, avifROStreamCurrent(&s), header.size, data->diag));
         }
 
         CHECK(avifROStreamSkip(&s, header.size));
@@ -2282,16 +2301,16 @@
     return AVIF_TRUE;
 }
 
-static avifBool avifParseMoovBox(avifDecoderData * data, const uint8_t * raw, size_t rawLen)
+static avifBool avifParseMoovBox(avifDecoderData * data, const uint8_t * raw, size_t rawLen, avifDiagnostics * diag)
 {
-    BEGIN_STREAM(s, raw, rawLen);
+    BEGIN_STREAM(s, raw, rawLen, diag, "Box[moov]");
 
     while (avifROStreamHasBytesLeft(&s, 1)) {
         avifBoxHeader header;
         CHECK(avifROStreamReadBoxHeader(&s, &header));
 
         if (!memcmp(header.type, "trak", 4)) {
-            CHECK(avifParseTrackBox(data, avifROStreamCurrent(&s), header.size));
+            CHECK(avifParseTrackBox(data, avifROStreamCurrent(&s), header.size, diag));
         }
 
         CHECK(avifROStreamSkip(&s, header.size));
@@ -2301,14 +2320,14 @@
 
 static avifBool avifParseFileTypeBox(avifFileType * ftyp, const uint8_t * raw, size_t rawLen, avifDiagnostics * diag)
 {
-    BEGIN_STREAM(s, raw, rawLen);
+    BEGIN_STREAM(s, raw, rawLen, diag, "Box[ftyp]");
 
     CHECK(avifROStreamRead(&s, ftyp->majorBrand, 4));
     CHECK(avifROStreamReadU32(&s, &ftyp->minorVersion));
 
     size_t compatibleBrandsBytes = avifROStreamRemainingBytes(&s);
     if ((compatibleBrandsBytes % 4) != 0) {
-        avifDiagnosticsPrintf(diag, "Box type 'ftyp' contains a compatible brands section that isn't divisible by 4 [%u]", compatibleBrandsBytes);
+        avifDiagnosticsPrintf(diag, "Box[ftyp] contains a compatible brands section that isn't divisible by 4 [%u]", compatibleBrandsBytes);
         return AVIF_FALSE;
     }
     ftyp->compatibleBrands = avifROStreamCurrent(&s);
@@ -2352,7 +2371,7 @@
         }
 
         // Parse the header, and find out how many bytes it actually was
-        BEGIN_STREAM(headerStream, headerContents.data, headerContents.size);
+        BEGIN_STREAM(headerStream, headerContents.data, headerContents.size, &decoder->diag, "File-level box header");
         avifBoxHeader header;
         CHECKERR(avifROStreamReadBoxHeaderPartial(&headerStream, &header), AVIF_RESULT_BMFF_PARSE_FAILED);
         parseOffset += headerStream.offset;
@@ -2392,7 +2411,7 @@
             metaSeen = AVIF_TRUE;
         } else if (!memcmp(header.type, "moov", 4)) {
             CHECKERR(!moovSeen, AVIF_RESULT_BMFF_PARSE_FAILED);
-            CHECKERR(avifParseMoovBox(data, boxContents.data, boxContents.size), AVIF_RESULT_BMFF_PARSE_FAILED);
+            CHECKERR(avifParseMoovBox(data, boxContents.data, boxContents.size, data->diag), AVIF_RESULT_BMFF_PARSE_FAILED);
             moovSeen = AVIF_TRUE;
         }
 
@@ -2436,7 +2455,7 @@
 
 avifBool avifPeekCompatibleFileType(const avifROData * input)
 {
-    BEGIN_STREAM(s, input->data, input->size);
+    BEGIN_STREAM(s, input->data, input->size, NULL, NULL);
 
     avifBoxHeader header;
     CHECK(avifROStreamReadBoxHeader(&s, &header));
@@ -2600,7 +2619,7 @@
 
             avifDecoderItem * item = avifMetaFindItem(decoder->data->meta, sample->itemID);
             avifROData itemContents;
-            avifResult readResult = avifDecoderItemRead(item, decoder->io, &itemContents, partialByteCount);
+            avifResult readResult = avifDecoderItemRead(item, decoder->io, &itemContents, partialByteCount, &decoder->diag);
             if (readResult != AVIF_RESULT_OK) {
                 return readResult;
             }
@@ -2755,12 +2774,14 @@
             break;
         }
         if (colorTrackIndex == data->tracks.count) {
+            avifDiagnosticsPrintf(&decoder->diag, "Failed to find AV1 color track");
             return AVIF_RESULT_NO_CONTENT;
         }
         colorTrack = &data->tracks.track[colorTrackIndex];
 
         colorProperties = avifSampleTableGetProperties(colorTrack->sampleTable);
         if (!colorProperties) {
+            avifDiagnosticsPrintf(&decoder->diag, "Failed to find AV1 color track's color properties");
             return AVIF_RESULT_BMFF_PARSE_FAILED;
         }
 
@@ -2843,6 +2864,7 @@
 
         if (data->meta->primaryItemID == 0) {
             // A primary item is required
+            avifDiagnosticsPrintf(&decoder->diag, "Primary item not specified");
             return AVIF_RESULT_NO_AV1_ITEMS_FOUND;
         }
 
@@ -2865,14 +2887,14 @@
                 // It's a thumbnail, skip it
                 continue;
             }
-            if ((data->meta->primaryItemID > 0) && (item->id != data->meta->primaryItemID)) {
-                // a primary item ID was specified, require it
+            if (item->id != data->meta->primaryItemID) {
+                // This is not the primary item, skip it
                 continue;
             }
 
             if (isGrid) {
                 avifROData readData;
-                avifResult readResult = avifDecoderItemRead(item, decoder->io, &readData, 0);
+                avifResult readResult = avifDecoderItemRead(item, decoder->io, &readData, 0, data->diag);
                 if (readResult != AVIF_RESULT_OK) {
                     return readResult;
                 }
@@ -2886,6 +2908,7 @@
         }
 
         if (!colorItem) {
+            avifDiagnosticsPrintf(&decoder->diag, "Primary item not found");
             return AVIF_RESULT_NO_AV1_ITEMS_FOUND;
         }
         colorProperties = &colorItem->properties;
@@ -2911,7 +2934,7 @@
             if (auxCProp && isAlphaURN(auxCProp->u.auxC.auxType) && (item->auxForID == colorItem->id)) {
                 if (isGrid) {
                     avifROData readData;
-                    avifResult readResult = avifDecoderItemRead(item, decoder->io, &readData, 0);
+                    avifResult readResult = avifDecoderItemRead(item, decoder->io, &readData, 0, data->diag);
                     if (readResult != AVIF_RESULT_OK) {
                         return readResult;
                     }
@@ -2998,12 +3021,12 @@
         decoder->alphaPresent = (alphaItem != NULL);
         decoder->image->alphaPremultiplied = decoder->alphaPresent && (colorItem->premByID == alphaItem->id);
 
-        avifResult colorItemValidationResult = avifDecoderItemValidateAV1(colorItem);
+        avifResult colorItemValidationResult = avifDecoderItemValidateAV1(colorItem, &decoder->diag);
         if (colorItemValidationResult != AVIF_RESULT_OK) {
             return colorItemValidationResult;
         }
         if (alphaItem) {
-            avifResult alphaItemValidationResult = avifDecoderItemValidateAV1(alphaItem);
+            avifResult alphaItemValidationResult = avifDecoderItemValidateAV1(alphaItem, &decoder->diag);
             if (alphaItemValidationResult != AVIF_RESULT_OK) {
                 return alphaItemValidationResult;
             }
diff --git a/src/stream.c b/src/stream.c
index 7e229e2..9b02e96 100644
--- a/src/stream.c
+++ b/src/stream.c
@@ -3,6 +3,8 @@
 
 #include "avif/internal.h"
 
+#include <assert.h>
+#include <inttypes.h>
 #include <stdint.h>
 #include <string.h>
 
@@ -14,10 +16,15 @@
     return stream->raw->data + stream->offset;
 }
 
-void avifROStreamStart(avifROStream * stream, avifROData * raw)
+void avifROStreamStart(avifROStream * stream, avifROData * raw, avifDiagnostics * diag, const char * diagContext)
 {
     stream->raw = raw;
     stream->offset = 0;
+    stream->diag = diag;
+    stream->diagContext = diagContext;
+
+    // If diag is non-NULL, diagContext must also be non-NULL
+    assert(!stream->diag || (stream->diag && stream->diagContext));
 }
 
 avifBool avifROStreamHasBytesLeft(const avifROStream * stream, size_t byteCount)
@@ -46,6 +53,9 @@
 avifBool avifROStreamSkip(avifROStream * stream, size_t byteCount)
 {
     if (!avifROStreamHasBytesLeft(stream, byteCount)) {
+        if (stream->diag) {
+            avifDiagnosticsPrintf(stream->diag, "%s: Failed to skip %zu bytes, truncated data?", stream->diagContext, byteCount);
+        }
         return AVIF_FALSE;
     }
     stream->offset += byteCount;
@@ -55,6 +65,9 @@
 avifBool avifROStreamRead(avifROStream * stream, uint8_t * data, size_t size)
 {
     if (!avifROStreamHasBytesLeft(stream, size)) {
+        if (stream->diag) {
+            avifDiagnosticsPrintf(stream->diag, "%s: Failed to read %zu bytes, truncated data?", stream->diagContext, size);
+        }
         return AVIF_FALSE;
     }
 
@@ -86,6 +99,9 @@
         *v = tmp;
     } else {
         // Unsupported factor
+        if (stream->diag) {
+            avifDiagnosticsPrintf(stream->diag, "%s: Failed to read UX8 value; Unsupported UX8 factor [%" PRIu64 "]", stream->diagContext, factor);
+        }
         return AVIF_FALSE;
     }
     return AVIF_TRUE;
@@ -125,6 +141,9 @@
         }
     }
     if (!foundNullTerminator) {
+        if (stream->diag) {
+            avifDiagnosticsPrintf(stream->diag, "%s: Failed to find a NULL terminator when reading a string", stream->diagContext);
+        }
         return AVIF_FALSE;
     }
 
@@ -162,6 +181,9 @@
 
     size_t bytesRead = stream->offset - startOffset;
     if ((size < bytesRead) || ((size - bytesRead) > SIZE_MAX)) {
+        if (stream->diag) {
+            avifDiagnosticsPrintf(stream->diag, "%s: Header size overflow check failure", stream->diagContext);
+        }
         return AVIF_FALSE;
     }
     header->size = (size_t)(size - bytesRead);
@@ -170,9 +192,7 @@
 
 avifBool avifROStreamReadBoxHeader(avifROStream * stream, avifBoxHeader * header)
 {
-    if (!avifROStreamReadBoxHeaderPartial(stream, header)) {
-        return AVIF_FALSE;
-    }
+    CHECK(avifROStreamReadBoxHeaderPartial(stream, header));
     return (header->size <= avifROStreamRemainingBytes(stream));
 }
 
@@ -193,7 +213,13 @@
 {
     uint8_t version;
     CHECK(avifROStreamReadVersionAndFlags(stream, &version, NULL));
-    return (version == enforcedVersion);
+    if (version != enforcedVersion) {
+        if (stream->diag) {
+            avifDiagnosticsPrintf(stream->diag, "%s: Expecting box version %u, got version %u", stream->diagContext, enforcedVersion, version);
+        }
+        return AVIF_FALSE;
+    }
+    return AVIF_TRUE;
 }
 
 // ---------------------------------------------------------------------------