iOfficeAI/OfficeCLI · error · ArgumentException
Image file '{path}' has extension .{ext} but magic bytes ind
Error message
Image file '{path}' has extension .{ext} but magic bytes indicate {ContentTypeName(sniffed)}. Rename or convert the file. What it means
Thrown by ImageSource.ResolveFile during magic-byte validation of raster formats (png, jpg, gif, bmp, tiff). The file's first bytes are sniffed and compared against the content type declared by the file extension; if they don't match (e.g. a .png file whose magic bytes are JPEG's FF D8 FF), the error is raised. This prevents embedding a corrupt or deliberately mislabeled image that would render incorrectly or cause Office to reject the document on reopen. SVG/EMF/WMF are intentionally skipped because they lack stable magic bytes.
Source
Thrown at src/officecli/Core/ImageSource.cs:85
{
if (!File.Exists(path))
throw new FileNotFoundException($"Image file not found: {path}");
var contentType = ExtensionToContentType(Path.GetExtension(path));
var ext = Path.GetExtension(path).TrimStart('.').ToLowerInvariant();
// Magic-byte validation for raster formats. SVG (XML) / EMF / WMF are
// intentionally skipped: SVG has no fixed magic, EMF/WMF have weaker
// headers and TrySniffContentType doesn't cover them. Only validate
// formats whose first 4 bytes are stable (png/jpg/gif/bmp/tiff).
var rasterExts = new[] { "png", "jpg", "jpeg", "gif", "bmp", "tif", "tiff" };
if (rasterExts.Contains(ext))
{
var bytes = File.ReadAllBytes(path);
if (TrySniffContentType(bytes, out var sniffed))
{
if (!IsCompatible(sniffed, contentType))
throw new ArgumentException(
$"Image file '{path}' has extension .{ext} but magic bytes indicate {ContentTypeName(sniffed)}. " +
"Rename or convert the file.");
}
else
{
throw new ArgumentException(
$"Image file '{path}' does not appear to be a valid {ext} file (magic bytes mismatch).");
}
return (new MemoryStream(bytes, writable: false), contentType);
}
return (File.OpenRead(path), contentType);
}
private static bool IsCompatible(PartTypeInfo sniffed, PartTypeInfo declared)
{
if (sniffed == declared) return true;
// jpg/jpeg are the same PartTypeInfo so this collapses naturally.View on GitHub (pinned to 1ced45e900)
Solutions
- Rename the file to match its actual format (e.g. if it's really JPEG, rename .png → .jpg).
- Convert the file to the declared format using an image tool (e.g. 'magick photo.png photo_real.png').
- Verify the file's true format with 'file photo.png' or a hex dump of the first bytes, then use the correct extension.
Example fix
// before — file is JPEG but named .png add image src='/tmp/photo.png' path='/body' // after — rename to match actual format mv /tmp/photo.png /tmp/photo.jpg add image src='/tmp/photo.jpg' path='/body'
Defensive patterns
Strategy: validation
Validate before calling
// Pre-check: verify the file's actual format matches its extension
using System.IO;
string ext = Path.GetExtension(path).TrimStart('.').ToLowerInvariant();
byte[] header = File.ReadAllBytes(path).Take(8).ToArray();
bool extMatchesMagic = (ext, header) switch
{
("png", var h) when h[0]==0x89 && h[1]==0x50 && h[2]==0x4E && h[3]==0x47 => true,
("jpg" or "jpeg", var h) when h[0]==0xFF && h[1]==0xD8 && h[2]==0xFF => true,
("gif", var h) when h[0]==0x47 && h[1]==0x49 && h[2]==0x46 && h[3]==0x38 => true,
("bmp", var h) when h[0]==0x42 && h[1]==0x4D => true,
_ => true, // assume OK for formats we don't sniff
};
if (!extMatchesMagic) Console.Error.WriteLine($"Warning: extension .{ext} doesn't match file content"); Try / catch
try
{
var (stream, contentType) = ImageSource.Resolve(path);
}
catch (ArgumentException ex) when (ex.Message.Contains("magic bytes indicate"))
{
// Rename file to match actual format, then retry
} Prevention
- Always convert images properly rather than renaming file extensions.
- After downloading an image, verify its actual format with 'file' command or magic-byte sniffing before embedding.
- In automated pipelines, normalize file extensions to match content using a detection step.
When it happens
Trigger: Passing a file named 'photo.png' that is actually a JPEG, or 'image.gif' that contains PNG data. The sniff succeeds (TrySniffContentType returns true) but IsCompatible(sniffed, declared) returns false because the sniffed format differs from what the extension declares. This is common with files that were renamed rather than converted.
Common situations: A file downloaded from the web where the server's content-type disagreed with the downloaded filename extension. A user who renamed 'photo.jpg' to 'photo.png' without converting the actual bytes. A pipeline that applies the wrong extension to a generated image. An adversarial input where the extension is spoofed to bypass a naive extension-based filter.
Related errors
- Image file '{path}' does not appear to be a valid {ext} file
- Invalid data URI: missing comma separator
- Only base64-encoded data URIs are supported
- Invalid data URI: empty base64 payload (would produce a 0-by
- Unsupported MIME type: {mime}. Supported: image/png, image/j
AI-assisted analysis of iOfficeAI/OfficeCLI@1ced45e900 (2026-08-13).
Data as JSON: /api/errors/ba01ba6542750579.
Report an issue: GitHub.