langflow-ai/langflow · error · HTTPException
Extension not found for file {file_name}
Error message
Extension not found for file {file_name} What it means
HTTP 500 from GET /files/download/{flow_id}/{file_name} when the file_name has no extension — i.e. file_name.split('.')[-1] equals the whole name because there is no dot. The router param is a ValidatedFileName, so in practice this only fires if the validator permits extensionless names; the guard is a last-resort check because content-type resolution is impossible without an extension.
Source
Thrown at src/backend/base/langflow/api/v1/files.py:136
folder = str(flow.id)
await storage_service.save_file(flow_id=folder, file_name=full_file_name, data=file_content)
return UploadFileResponse(flow_id=str(flow.id), file_path=f"{folder}/{full_file_name}")
except Exception as e:
raise HTTPException(status_code=500, detail=str(e)) from e
@router.get("/download/{flow_id}/{file_name}")
async def download_file(
file_name: ValidatedFileName,
flow: Annotated[Flow, Depends(get_flow)],
storage_service: Annotated[StorageService, Depends(get_storage_service)],
):
# Authorization handled by get_flow dependency
flow_id_str = str(flow.id)
extension = file_name.split(".")[-1]
if not extension:
raise HTTPException(status_code=500, detail=f"Extension not found for file {file_name}")
try:
content_type = build_content_type_from_extension(extension)
except Exception as e:
raise HTTPException(status_code=500, detail=str(e)) from e
if not content_type:
raise HTTPException(status_code=500, detail=f"Content type not found for extension {extension}")
try:
file_content = await storage_service.get_file(flow_id=flow_id_str, file_name=file_name)
headers = {
"Content-Disposition": build_content_disposition(file_name),
"Content-Type": "application/octet-stream",
"Content-Length": str(len(file_content)),
}
return StreamingResponse(BytesIO(file_content), media_type=content_type, headers=headers)
except Exception as e:
raise HTTPException(status_code=500, detail=str(e)) from eView on GitHub (pinned to 976ec789d2)
Solutions
- Request the file by its exact stored name including extension (the upload response's file_path contains it)
- Ensure uploaded files always carry an extension; add one at upload time if the original lacks it
- List the flow's files to recover the exact file_name before constructing the download URL
Example fix
# before
GET /api/v1/files/download/{flow_id}/data
# after
GET /api/v1/files/download/{flow_id}/2024-01-01_00-00-00_data.csv Defensive patterns
Strategy: validation
Validate before calling
from pathlib import Path
def has_extension(file_name: str) -> bool:
return bool(Path(file_name).suffix) Type guard
def is_downloadable_name(file_name: str) -> bool:
name = file_name.strip()
return "." in name and name.rsplit(".", 1)[1].isalnum() Try / catch
try:
client.get(f"/files/download/{flow_id}/{file_name}").raise_for_status()
except HTTPError as e:
if "Extension not found" in e.response.text:
file_name = lookup_stored_name(flow_id, file_name) # find real stored name
client.get(f"/files/download/{flow_id}/{file_name}").raise_for_status() Prevention
- Build download URLs only from the file_path returned at upload
- Require an extension when storing files
When it happens
Trigger: GET /files/download/{flow_id}/README (no dot in name); filenames ending in a dot where split yields an empty-ish segment; URLs where the filename got truncated before the extension.
Common situations: Hand-constructed download URLs; files saved without extensions by external tooling; URL-encoding issues stripping the extension.
Related errors
- Content type not found for extension {extension}
- str(e)
- {str(e)}
- Content type {content_type} is not an image
- HTTP error! status: ${response.status}
AI-assisted analysis of langflow-ai/langflow@976ec789d2 (2026-08-14).
Data as JSON: /api/errors/cc93665eba2ba8c3.
Report an issue: GitHub.