{"record":{"id":"48e058b5e90eb30c","repo":"Zie619/n8n-workflows","slug":"workflow-file-filename-not-found-on-filesystem","errorCode":null,"errorMessage":"Workflow file '{filename}' not found on filesystem","messagePattern":"Workflow file '(.+?)' not found on filesystem","errorType":"http","errorClass":"HTTPException","httpStatus":404,"severity":"error","filePath":"api_server.py","lineNumber":355,"sourceCode":"        matching_file = None\n        for subdir in workflows_path.iterdir():\n            if subdir.is_dir():\n                target_file = subdir / filename\n                if target_file.exists() and target_file.is_file():\n                    # Verify the file is actually within workflows directory\n                    try:\n                        target_file.resolve().relative_to(workflows_path)\n                        matching_file = target_file\n                        break\n                    except ValueError:\n                        print(\n                            f\"Security: Blocked access to file outside workflows: {target_file}\"\n                        )\n                        continue\n\n        if not matching_file:\n            print(f\"Warning: File {filename} not found in workflows directory\")\n            raise HTTPException(\n                status_code=404,\n                detail=f\"Workflow file '{filename}' not found on filesystem\",\n            )\n\n        with open(matching_file, \"r\", encoding=\"utf-8\") as f:\n            raw_json = json.load(f)\n\n        return {\"metadata\": workflow_meta, \"raw_json\": raw_json}\n    except HTTPException:\n        raise\n    except Exception as e:\n        raise HTTPException(status_code=500, detail=f\"Error loading workflow: {str(e)}\")\n\n\n@app.get(\"/api/workflows/{filename}/download\")\nasync def download_workflow(filename: str, request: Request):\n    \"\"\"Download workflow JSON file with security validation.\"\"\"\n    try:","sourceCodeStart":337,"sourceCodeEnd":373,"githubUrl":"https://github.com/Zie619/n8n-workflows/blob/94007c1445d9258a7da116646b79473e7c7c3282/api_server.py#L337-L373","documentation":"A 404 from GET /api/workflows/{filename} raised after the metadata was found in the database but no matching file was located on disk. The handler iterates only immediate subdirectories of workflows/ (subdir / filename) and additionally requires the resolved path to stay inside workflows_path; if no subdirectory contains the file, matching_file stays None and this error is raised (server logs 'Warning: File {filename} not found in workflows directory').","triggerScenarios":"The DB is stale: it indexes a file that was deleted, moved, or renamed on disk. Also triggered by files placed directly at workflows/ top level (only subdirectories are scanned, one level deep) or by symlinked subdirectories whose targets resolve outside workflows_path.","commonSituations":"Files deleted or renamed after indexing; a manual DB edit; directory restructuring that flattened or deepened the layout beyond one subdirectory level.","solutions":["Confirm the file still exists under a subdirectory of workflows/ (e.g. workflows/<category>/<file>.json) and restore it if moved.","Reindex after any filesystem change so the DB and disk agree.","If you need top-level or nested-deeper files, adjust the search loop in api_server.py to also check workflows_path / filename or recurse.","Avoid symlinks pointing outside workflows/ — they fail the resolve().relative_to(workflows_path) containment check by design."],"exampleFix":"# before\nfor subdir in workflows_path.iterdir():\n    if subdir.is_dir():\n        target_file = subdir / filename\n\n# after (also accept top-level files)\nfor subdir in workflows_path.iterdir():\n    target = subdir / filename if subdir.is_dir() else None\n    if target and target.exists() and target.is_file():\n        matching_file = target\n        break\nif not matching_file:\n    top = workflows_path / filename\n    if top.exists() and top.is_file():\n        matching_file = top","handlingStrategy":"validation","validationCode":"from pathlib import Path\n\ndef file_findable(workflows_dir, filename):\n    root = Path(workflows_dir).resolve()\n    return any(\n        (sub / filename).is_file() and str((sub / filename).resolve()).startswith(str(root))\n        for sub in root.iterdir() if sub.is_dir()\n    )","typeGuard":null,"tryCatchPattern":"try:\n    data = client.get(f'/api/workflows/{name}').json()\nexcept HTTPError as e:\n    if e.response.status_code == 404:\n        # index says yes, disk says no: check layout, then reindex\n        assert file_findable('workflows', name), 'file missing or misplaced'","preventionTips":["Keep every workflow exactly one subdirectory below workflows/.","Reindex after moving, renaming, or deleting files.","Avoid symlinks pointing outside workflows/."],"tags":["fastapi","filesystem","stale-index","http-404"],"backgroundTag":null,"analyzedSha":"94007c1445d9258a7da116646b79473e7c7c3282","analyzedAt":"2026-08-15T04:10:37.591Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}