affaan-m/ECC · warning · ValidationError
File extension does not match file content.
Error message
File extension does not match file content.
What it means
After the MIME passes the allowlist, validate_file_type() cross-checks that the uploaded file's extension is one of those associated with the detected MIME (via MIME_TO_EXTENSIONS). This catches content/extension mismatches — e.g. a PDF renamed to .png, or a jpeg with a .gif extension — and is a defense against polyglot/mislabel attacks layered on top of the magic-byte check. A legitimate but unusual extension (e.g. .jpe for jpeg) will also trip this until added to the map.
Source
Thrown at skills/django-security/SKILL.md:419
MIME_TO_EXTENSIONS = {
'image/jpeg': {'.jpg', '.jpeg'},
'image/png': {'.png'},
'image/gif': {'.gif'},
'application/pdf': {'.pdf'},
}
def validate_file_type(value):
"""Validate file type using magic bytes and cross-check extension."""
mime = magic.from_buffer(value.read(2048), mime=True)
value.seek(0)
if mime not in ALLOWED_MIMES:
raise ValidationError('Unsupported file type.')
ext = os.path.splitext(value.name)[1].lower()
if ext not in MIME_TO_EXTENSIONS.get(mime, set()):
raise ValidationError('File extension does not match file content.')
def validate_file_size(value):
"""Validate file size (max 5MB)."""
if value.size > 5 * 1024 * 1024:
raise ValidationError('File too large. Max size is 5MB.')
# models.py
class Document(models.Model):
file = models.FileField(
upload_to='documents/',
validators=[validate_file_type, validate_file_size]
)
```
For environments where installing libmagic is difficult (e.g., minimal containers),
use the pure-Python `filetype` package as an alternative:
View on GitHub (pinned to 01e15490f0)
Solutions
- Rename the file so its extension matches its actual content type before upload.
- If the extension is legitimately valid for that MIME, add it to MIME_TO_EXTENSIONS (e.g. add '.jpe' to image/jpeg).
- Normalize extensions server-side from the detected MIME instead of rejecting.
- Reject with a clear user-facing message asking to re-save with the correct extension.
Example fix
// before — user uploads photo.jpg that is actually a PDF
# raises: File extension does not match file content.
// after — normalize extension from detected MIME server-side
import os
MIME_TO_EXT = {'image/jpeg': '.jpg', 'image/png': '.png', 'image/gif': '.gif', 'application/pdf': '.pdf'}
def normalize_extension(value, detected_mime: str) -> None:
expected = MIME_TO_EXT.get(detected_mime)
if expected:
value.name = os.path.splitext(value.name)[0] + expected Defensive patterns
Strategy: validation
Validate before calling
import os, magic
MIME_TO_EXTENSIONS = {
'image/jpeg': {'.jpg', '.jpeg'},
'image/png': {'.png'},
'image/gif': {'.gif'},
'application/pdf': {'.pdf'},
}
def extension_matches_content(path: str, filename: str) -> bool:
with open(path, "rb") as f:
mime = magic.from_buffer(f.read(2048), mime=True)
ext = os.path.splitext(filename)[1].lower()
return ext in MIME_TO_EXTENSIONS.get(mime, set()) Type guard
from django.core.exceptions import ValidationError
def is_extension_mismatch(exc: BaseException) -> bool:
return isinstance(exc, ValidationError) and "extension does not match" in str(exc) Try / catch
from django.core.exceptions import ValidationError
try:
validate_file_type(uploaded)
except ValidationError as e:
if "extension does not match" in str(e):
return JsonResponse({"error": "Rename file to match its content type"}, status=400)
raise Prevention
- Detect MIME first, then assign the canonical extension server-side.
- Keep MIME_TO_EXTENSIONS exhaustive for each allowed MIME.
- Treat extension/content mismatch as suspicious — log it.
- Don't trust the client-supplied filename extension.
When it happens
Trigger: A file whose magic bytes say image/jpeg but whose name ends in .png; a PDF renamed to .jpg; any case where os.path.splitext(name) yields an extension not in MIME_TO_EXTENSIONS[mime].
Common situations: Users renaming files to bypass extension filters; automated conversions that keep the old extension; polyglot upload attempts; a legitimate file with an unusual-but-valid extension not present in the map.
Related errors
- Unsupported file type.
- File too large. Max size is 5MB.
- Invalid ECC repo root: missing install script at ${installAp
- TypeScript compiler not found. Install root dev dependencies
- Missing value for --target
AI-assisted analysis of affaan-m/ECC@01e15490f0 (2026-08-13).
Data as JSON: /api/errors/172a8b5ef2cd8958.
Report an issue: GitHub.