Zie619/n8n-workflows · error · HTTPException

Error downloading workflow: {str(e)}

Error message

Error downloading workflow: {str(e)}

What it means

The catch-all 500 for GET /api/workflows/{filename}/download. HTTPExceptions re-raise untouched, so reaching this handler means an unexpected exception while building or serving the FileResponse — e.g. the file disappearing between the scan and the response, or FileResponse failing to stat/open the path. The exception is printed server-side ('Error downloading workflow {filename}: ...') and wrapped into the 500 detail.

Source

Thrown at api_server.py:430

        file_path = json_files[0]

        # Final security check: Ensure file is within workflows directory
        try:
            file_path.resolve().relative_to(workflows_path)
        except ValueError:
            print(
                f"Security: Blocked final attempt to access file outside workflows: {file_path}"
            )
            raise HTTPException(status_code=403, detail="Access denied")

        return FileResponse(
            str(file_path), media_type="application/json", filename=filename
        )
    except HTTPException:
        raise
    except Exception as e:
        print(f"Error downloading workflow {filename}: {str(e)}")
        raise HTTPException(
            status_code=500, detail=f"Error downloading workflow: {str(e)}"
        )


@app.get("/api/workflows/{filename}/diagram")
async def get_workflow_diagram(filename: str, request: Request):
    """Get Mermaid diagram code for workflow visualization."""
    try:
        # Security: Validate filename to prevent path traversal
        if not validate_filename(filename):
            print(f"Security: Blocked path traversal attempt for filename: {filename}")
            raise HTTPException(status_code=400, detail="Invalid filename format")

        # Security: Rate limiting
        client_ip = request.client.host if request.client else "unknown"
        if not check_rate_limit(client_ip):
            raise HTTPException(
                status_code=429, detail="Rate limit exceeded. Please try again later."

View on GitHub (pinned to 94007c1445)

Solutions

  1. Read the detail text and the server console for the exact exception.
  2. Verify the file is readable by the server process user: ls -l workflows/<subdir>/<file> and sudo -u <user> cat <path>.
  3. Re-run the listing and retry the download with a filename known to currently exist.
  4. For recurring races, index/read files under a stable snapshot or pause cleanup during reindex.

Example fix

# before
return FileResponse(str(file_path), media_type='application/json', filename=filename)

# after (fail with a clear 410 when the file vanished mid-request)
if not file_path.is_file():
    raise HTTPException(status_code=410, detail='Workflow file no longer available')
return FileResponse(str(file_path), media_type='application/json', filename=filename)
Defensive patterns

Strategy: try-catch

Validate before calling

import requests

def file_served(base, name):
    r = requests.head(f'{base}/api/workflows/{name}/download', timeout=5,
                      allow_redirects=True)
    return r.status_code == 200

Try / catch

try:
    save(client.get(f'/api/workflows/{name}/download'))
except HTTPError as e:
    if e.response.status_code == 500:
        log(e.response.json()['detail'])  # contains the underlying OS error
        revalidate_file_on_disk(name)

Prevention

When it happens

Trigger: TOCTOU: file exists during the scan but is deleted before FileResponse opens it; permission error when the server process reads the file; FileResponse construction failing on a path that is actually a directory or a broken symlink that passed earlier checks.

Common situations: Concurrent cleanup scripts pruning workflows/; permissions changed while the server runs; exotic filesystems where stat succeeds but open fails.

Related errors


AI-assisted analysis of Zie619/n8n-workflows@94007c1445 (2026-08-15). Data as JSON: /api/errors/2627940c409ce458. Report an issue: GitHub.