#1428·Robyn

serve_file() sends a literal 'Content-Type: None' header for unrecognized file extensions

Author: BitWeaverDevCreated Jul 31, 2026Updated Jul 31, 2026

Bug Description

serve_file() guesses the MIME type from the filename and passes it straight into Headers without handling the case where guessing fails:

https://github.com/sparckles/Robyn/blob/main/robyn/responses.py#L47-L64

python
mime_type = mimetypes.guess_type(file_name)[0]
headers = Headers({"Content-Type": mime_type})

mimetypes.guess_type() returns None for any file whose extension it doesn't recognize (e.g. .log, .dat, extensionless files, many less-common formats). That None is then passed into Headers({...}), whose Rust constructor (src/types/headers.rs) builds the header value via value.to_string() — and in Python, str(None) is the literal string "None". The result is a real HTTP response header Content-Type: None sent to the client, instead of a sensible fallback like application/octet-stream.

Steps to Reproduce

python
from robyn.responses import serve_file

resp = serve_file("/tmp/some_file.unknownext")
print(resp.headers.get("Content-Type"))  # "None" (the string), not a real content type

Or end-to-end: register a route that does return serve_file(path) for a file with an unrecognized extension, and inspect the response headers — Content-Type will literally be the string None.

Expected vs Actual

  • Expected: files with an unrecognized extension get a sane fallback Content-Type, e.g. application/octet-stream.
  • Actual: the client receives a literal Content-Type: None header.

Suggested Fix

python
mime_type = mimetypes.guess_type(file_name)[0] or "application/octet-stream"

Additional Info

Found via a broader codebase audit while working on #485 (response compression). Robyn version: main branch.