Skip to content

[bug]: Issue attachment upload returns 400 for files with empty or unrecognized MIME types (.md, .yaml, .log, etc.) #8847

@Amideus-dr

Description

@Amideus-dr

Is there an existing issue for this?

  • I have searched the existing issues

Current behavior

When uploading issue attachments via the Plane UI, certain file types (e.g., .md, .yaml, .yml, .log, .docx) fail with HTTP 400 "Invalid file type" even though they are perfectly valid files.

Root cause: The browser's File.type property returns an empty string for file extensions that don't have a well-known MIME type mapping in the browser's internal registry. The frontend sends this empty string as the type field in the POST request body.

In IssueAttachmentV2Endpoint.post() (file plane/app/views/issue/attachment.py), the validation at line ~100 is:

type = request.data.get("type", False)
# ...
if not type or type not in settings.ATTACHMENT_MIME_TYPES:
    return Response(
        {"error": "Invalid file type.", "status": False},
        status=status.HTTP_400_BAD_REQUEST,
    )

When type is an empty string "", not type evaluates to True in Python (empty string is falsy), so the request is immediately rejected — regardless of the file extension or actual content.

Additionally, even when the browser does provide a MIME type (e.g., text/markdown for .md files on some systems), this type is not included in settings.ATTACHMENT_MIME_TYPES, causing the same 400 error.

Expected behavior

File attachments with common extensions like .md, .yaml, .log, .docx should upload successfully. If the browser doesn't provide a MIME type, the backend should infer it from the filename extension.

Steps to reproduce

  1. Open any issue in Plane (self-hosted v1.2.3)
  2. Click "Add Attachment"
  3. Select a .md file (e.g., README.md)
  4. Upload fails with 400 error in the network tab: {"error": "Invalid file type.", "status": false}

This also affects .yaml, .yml, .log, and other extensions where the browser returns an empty MIME type.

Suggested fix

In plane/app/views/issue/attachment.py, add MIME type inference from the filename when the browser-provided type is empty or not in the allowlist:

name = request.data.get("name")
type = request.data.get("type", False)
size = int(request.data.get("size", settings.FILE_SIZE_LIMIT))

# Infer MIME type from filename when browser sends empty or unrecognized type
if (not type or type not in settings.ATTACHMENT_MIME_TYPES) and name:
    import mimetypes
    guessed = mimetypes.guess_type(name)[0]
    if guessed and guessed in settings.ATTACHMENT_MIME_TYPES:
        type = guessed
    elif guessed and guessed.startswith("text/"):
        type = "text/plain"  # Safe fallback for text-based files
    else:
        type = "application/octet-stream"  # Generic binary fallback

if not type or type not in settings.ATTACHMENT_MIME_TYPES:
    return Response(
        {"error": "Invalid file type.", "status": False},
        status=status.HTTP_400_BAD_REQUEST,
    )

This approach:

  • Uses Python's mimetypes module (stdlib, no dependencies) to infer the type from the filename
  • Maps recognized-but-not-allowlisted text types (like text/markdown) to text/plain
  • Falls back to application/octet-stream for completely unknown types
  • Both text/plain and application/octet-stream are already in ATTACHMENT_MIME_TYPES
  • Does not weaken security — the allowlist check still runs after inference

Version

v1.2.3 (self-hosted, Docker)

Browser & OS

Chrome 146, Linux (also reproduced on Windows)

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions