From 3200f0133ac556e04077882840d2e8bbcb4113d5 Mon Sep 17 00:00:00 2001 From: Danny Avila Date: Fri, 4 Sep 2026 13:50:02 -0400 Subject: [PATCH] =?UTF-8?q?=F0=9F=9A=AB=20fix:=20Decline=20to=20Name=20a?= =?UTF-8?q?=20Heif=20Container=20With=20Unreported=20Compression=20(#15612?= =?UTF-8?q?)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `resolveImageMimeType` returned `image/heic` for any heif container whose compression was not `av1`, which included the case where sharp reported no compression at all. `Metadata.compression` is optional, so unreported is a real outcome, and answering it with HEIC is a guess — an AVIF read back without its compression would be recorded as HEIC. That is the failure #15606 exists to prevent, reproduced inside the fix for it, and it contradicted the documented contract of returning `undefined` rather than naming bytes that cannot be identified. Unreported or unrecognized compression now yields `undefined`, which leaves `saveBase64Image` on the declared type: still only a claim, but the caller's claim rather than one invented here. --- packages/api/src/files/mime.spec.ts | 5 +++++ packages/api/src/files/mime.ts | 10 +++++++--- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/packages/api/src/files/mime.spec.ts b/packages/api/src/files/mime.spec.ts index b841c7f7eb..9f28fb170b 100644 --- a/packages/api/src/files/mime.spec.ts +++ b/packages/api/src/files/mime.spec.ts @@ -40,4 +40,9 @@ describe('resolveImageMimeType', () => { expect(resolveImageMimeType({ format: 'svg' })).toBeUndefined(); expect(resolveImageMimeType({ format: 'raw' })).toBeUndefined(); }); + + it('declines to name a heif container whose compression sharp did not report', () => { + expect(resolveImageMimeType({ format: 'heif' })).toBeUndefined(); + expect(resolveImageMimeType({ format: 'heif', compression: undefined })).toBeUndefined(); + }); }); diff --git a/packages/api/src/files/mime.ts b/packages/api/src/files/mime.ts index a4bebb9fe8..1fa85a46d0 100644 --- a/packages/api/src/files/mime.ts +++ b/packages/api/src/files/mime.ts @@ -27,8 +27,9 @@ export type EncodedImageMetadata = Pick; * bytes leaves a file whose `type` misdescribes its own contents, and that type is later handed * to providers verbatim as `media_type`/`mimeType`, so it has to describe the bytes on disk. * - * Returns `undefined` when sharp reports a format with no media type of its own, leaving the - * caller to decide what to record rather than guessing here. + * Returns `undefined` when the bytes cannot be named confidently — a format with no media type of + * its own, or a heif container whose compression sharp did not report — leaving the caller to fall + * back to what it already knows. Naming one of those anyway would be the guess this exists to stop. */ export function resolveImageMimeType(metadata: EncodedImageMetadata): string | undefined { const { format } = metadata; @@ -36,7 +37,10 @@ export function resolveImageMimeType(metadata: EncodedImageMetadata): string | u return undefined; } if (format === 'heif') { - return metadata.compression === 'av1' ? 'image/avif' : 'image/heic'; + if (metadata.compression === 'av1') { + return 'image/avif'; + } + return metadata.compression === 'hevc' ? 'image/heic' : undefined; } return SHARP_FORMAT_MIME_TYPES[format]; }