This is an issue when flask.send_file() is called with a file-like object without providing either the mimetype or download_name parameter.

Why is this an issue?

Flask’s send_file() function needs to determine the content type for HTTP responses. When you pass a file-like object, such as an open file handle, BytesIO, or StringIO, Flask cannot infer the content type automatically. Providing either mimetype or download_name resolves the issue. When download_name is provided, Flask can infer the mimetype via the file extension.

Without the mimetype parameter, Flask doesn’t know what Content-Type header to set for the response. Without download_name, Flask cannot set the Content-Disposition header properly for downloads.

When Flask encounters this situation, it raises a ValueError with a message indicating that it cannot determine the content type. This causes your application to crash with a 500 Internal Server Error, creating a poor user experience.

What is the potential impact?

The application will crash with a ValueError when the endpoint is accessed, resulting in a 500 Internal Server Error response. This breaks the file download functionality and creates a poor user experience.

How to fix it in Flask

Add the mimetype parameter to specify the content type of the file being sent.

Code examples

Noncompliant code example

from flask import send_file

def download_file():
    file_obj = open('data.txt', 'rb')
    return send_file(file_obj)  # Noncompliant

Compliant solution

from flask import send_file

def download_file():
    file_obj = open('data.txt', 'rb')
    return send_file(file_obj, mimetype='text/plain')

Add the download_name parameter to specify the filename for downloads. This is especially useful for in-memory file objects. For Flask versions prior to 2.0, use attachment_filename instead.

Noncompliant code example

from flask import send_file
from io import BytesIO

def download_csv():
    csv_data = BytesIO(b'name,age\nJohn,30')
    return send_file(csv_data)  # Noncompliant

Compliant solution

from flask import send_file
from io import BytesIO

def download_csv():
    csv_data = BytesIO(b'name,age\nJohn,30')
    return send_file(csv_data, download_name='data.csv', as_attachment=True)

You can provide both parameters for complete control over the response headers.

Noncompliant code example

from flask import send_file
from io import StringIO

def download_log():
    log_data = StringIO('INFO: Application started')
    return send_file(log_data)  # Noncompliant

Compliant solution

from flask import send_file
from io import StringIO

def download_log():
    log_data = StringIO('INFO: Application started')
    return send_file(log_data, mimetype='text/plain', download_name='app.log')

Resources

Documentation