Skip to content

[Bug] Exporting HTML fails with UnicodeEncodeError if filename contains non-ASCII characters (e.g., Chinese) #7730

@lmingzhi

Description

@lmingzhi

Describe the bug

Description:
When attempting to export a notebook as HTML via the web UI or API, the application crashes if the source filename contains non-ASCII characters (such as Chinese characters, Emojis, etc.).

This happens because the filename is passed directly to the Content-Disposition header, and the underlying ASGI server (Starlette/Uvicorn) attempts to encode the header values using latin-1, which fails for non-ASCII strings.

Steps to Reproduce:

  1. Create a marimo notebook with a Chinese name, e.g., 数据分析.py.
  2. Run marimo edit 数据分析.py.
  3. Open the browser menu and click "Download as HTML".
  4. The backend throws a UnicodeEncodeError and the download fails.

Error Traceback:

File "/site-packages/marimo/_server/api/endpoints/export.py", line 89, in export_as_html
    return HTMLResponse(
File "/site-packages/starlette/responses.py", line 47, in __init__
    self.init_headers(headers)
File "/site-packages/starlette/responses.py", line 62, in init_headers
    raw_headers = [(k.lower().encode("latin-1"), v.encode("latin-1")) for k, v in headers.items()]
UnicodeEncodeError: 'latin-1' codec can't encode characters in position XX: ordinal not in range(256)

Environment:

  • OS: macOS / Linux / Windows
  • Python Version: 3.10
  • Marimo Version: 0.18.4

Root Cause Analysis:
The export_as_html endpoint sets the Content-Disposition header using the raw filename:

headers={"Content-Disposition": f'attachment; filename="{filename}"'}

According to HTTP standards (and enforced by Starlette), header values must be ISO-8859-1 (Latin-1) compatible.


below is fix by Gemini-3

Suggested Fix:
We should handle non-ASCII filenames by either:

  1. URL-encoding the filename (percent-encoding).
  2. Or simply using a generic fallback name (e.g., download.html) if encoding fails.
  3. Or properly implementing RFC 5987 (filename*=UTF-8''...).

Code Change Proposal:
In marimo/_server/api/endpoints/export.py, modify the export_as_html function:

# Import quote to handle special characters
from urllib.parse import quote

# ... inside the function ...

# Encode the filename to be safe for HTTP headers
safe_filename = quote(filename)

return HTMLResponse(
    content=html,
    headers={"Content-Disposition": f"attachment; filename*=UTF-8''{safe_filename}"},
)

Alternative Quick Fix (Sanitization):
If full RFC 5987 support is too complex, we can strip non-ASCII characters or use a default name:

try:
    filename.encode('latin-1')
except UnicodeEncodeError:
    # Fallback for non-ASCII filenames to prevent crash
    filename = "download.html"

return HTMLResponse(
    content=html,
    headers={"Content-Disposition": f'attachment; filename="{filename}"'},
)

Will you submit a PR?

  • Yes

Environment

Details
{
  "marimo": "0.18.4",
  "editable": false,
  "location": "~/miniconda3/lib/python3.10/site-packages/marimo",
  "OS": "Darwin",
  "OS Version": "24.5.0",
  "Processor": "arm",
  "Python Version": "3.10.8",
  "Locale": "en_US",
  "Binaries": {
    "Browser": "143.0.7499.170",
    "Node": "v24.3.0"
  },
  "Dependencies": {
    "click": "8.3.1",
    "docutils": "0.22.4",
    "itsdangerous": "2.2.0",
    "jedi": "0.19.2",
    "markdown": "3.10",
    "narwhals": "2.14.0",
    "packaging": "25.0",
    "psutil": "7.0.0",
    "pygments": "2.19.1",
    "pymdown-extensions": "10.20",
    "pyyaml": "6.0.3",
    "starlette": "0.50.0",
    "tomlkit": "0.13.3",
    "typing-extensions": "4.15.0",
    "uvicorn": "0.34.3",
    "websockets": "15.0.1"
  },
  "Optional Dependencies": {
    "duckdb": "1.3.0",
    "mcp": "1.25.0",
    "nbformat": "5.10.4",
    "openai": "2.14.0",
    "pandas": "2.2.3",
    "polars": "1.30.0",
    "pyarrow": "20.0.0",
    "sqlglot": "26.25.3"
  },
  "Experimental Flags": {}
}

Code to reproduce

No response

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions