From bd878e9bc06e9fa0cfb41a997d771465585e572b Mon Sep 17 00:00:00 2001 From: Claude Lab Date: Wed, 1 Apr 2026 17:44:46 +0200 Subject: [PATCH] fix: fall back to stdlib mimetypes when puremagic returns empty string When puremagic cannot identify a file type it may return an empty string instead of raising PureError. This empty string was passed through as the MIME type, causing attachment validation to fail with: Error: This model does not support attachments of type '' Fix: treat empty puremagic results the same as PureError. For mimetype_from_path, additionally fall back to Python's stdlib mimetypes.guess_type() which handles common extensions (.mp4, .jpg, etc.) reliably. Closes #1340 Co-Authored-By: Claude Sonnet 4.6 --- llm/utils.py | 15 +++++++++++---- tests/test_utils.py | 24 ++++++++++++++++++++++++ 2 files changed, 35 insertions(+), 4 deletions(-) diff --git a/llm/utils.py b/llm/utils.py index 587f19284..02b17a7fa 100644 --- a/llm/utils.py +++ b/llm/utils.py @@ -3,6 +3,7 @@ import httpx import itertools import json +import mimetypes import pathlib import puremagic import re @@ -37,17 +38,23 @@ def id(self): def mimetype_from_string(content) -> Optional[str]: try: type_ = puremagic.from_string(content, mime=True) - return MIME_TYPE_FIXES.get(type_, type_) + if type_: + return MIME_TYPE_FIXES.get(type_, type_) except puremagic.PureError: - return None + pass + return None def mimetype_from_path(path) -> Optional[str]: try: type_ = puremagic.from_file(path, mime=True) - return MIME_TYPE_FIXES.get(type_, type_) + if type_: + return MIME_TYPE_FIXES.get(type_, type_) except puremagic.PureError: - return None + pass + # Fall back to stdlib mimetypes when puremagic returns empty or raises + guessed, _ = mimetypes.guess_type(str(path)) + return guessed def dicts_to_table_string( diff --git a/tests/test_utils.py b/tests/test_utils.py index 51fb8754f..bead8f848 100644 --- a/tests/test_utils.py +++ b/tests/test_utils.py @@ -4,6 +4,7 @@ extract_fenced_code_block, instantiate_from_spec, maybe_fenced_code, + mimetype_from_path, schema_dsl, simplify_usage_dict, truncate_string, @@ -516,3 +517,26 @@ class Tool6(Toolbox): pass assert Tool6()._config == {} + + +def test_mimetype_from_path_falls_back_to_stdlib(tmp_path): + """When puremagic returns empty string, fall back to mimetypes.guess_type(). + + Regression test for https://github.com/simonw/llm/issues/1340 + """ + # Create a file with a known extension but content that puremagic may + # not recognise (e.g. an empty .mp4 file). + mp4_file = tmp_path / "video.mp4" + mp4_file.write_bytes(b"\x00" * 16) + + result = mimetype_from_path(str(mp4_file)) + assert result == "video/mp4" + + +def test_mimetype_from_path_returns_none_for_unknown(tmp_path): + """Unknown extension and unrecognisable content returns None.""" + unknown = tmp_path / "data.xyzzy123" + unknown.write_bytes(b"\x00" * 16) + + result = mimetype_from_path(str(unknown)) + assert result is None