{"record":{"id":"6609cea3ac2e51a0","repo":"ATH-MaaS/Pixelle-Video","slug":"path-is-not-a-file-file-path","errorCode":null,"errorMessage":"Path is not a file: {file_path}","messagePattern":"Path is not a file: (.+?)","errorType":"http","errorClass":"HTTPException","httpStatus":400,"severity":"error","filePath":"api/routers/files.py","lineNumber":81,"sourceCode":"        \n        # Check if path starts with allowed prefix, otherwise try output/\n        full_path = None\n        for prefix in allowed_prefixes:\n            if file_path.startswith(prefix):\n                full_path = file_path\n                break\n        \n        # If no prefix matched, assume it's in output/ (backward compatibility)\n        if full_path is None:\n            full_path = f\"output/{file_path}\"\n        \n        abs_path = Path.cwd() / full_path\n        \n        if not abs_path.exists():\n            raise HTTPException(status_code=404, detail=f\"File not found: {file_path}\")\n        \n        if not abs_path.is_file():\n            raise HTTPException(status_code=400, detail=f\"Path is not a file: {file_path}\")\n        \n        # Security: only allow access to specified directories\n        try:\n            rel_path = abs_path.relative_to(Path.cwd())\n            rel_path_str = str(rel_path)\n            \n            # Check if path starts with any allowed prefix\n            is_allowed = any(rel_path_str.startswith(prefix.rstrip('/')) for prefix in allowed_prefixes)\n            \n            if not is_allowed:\n                raise HTTPException(\n                    status_code=403, \n                    detail=f\"Access denied: only {', '.join(p.rstrip('/') for p in allowed_prefixes)} directories are accessible\"\n                )\n        except ValueError:\n            raise HTTPException(status_code=403, detail=\"Access denied\")\n        \n        # Determine media type","sourceCodeStart":63,"sourceCodeEnd":99,"githubUrl":"https://github.com/ATH-MaaS/Pixelle-Video/blob/848b054e4fae40dabc62ec58e960b573e83793ac/api/routers/files.py#L63-L99","documentation":"After confirming existence, the handler checks abs_path.is_file() and returns 400 when the path exists but is a directory (or other non-regular file). This endpoint only serves file contents, so directories are rejected.","triggerScenarios":"GET to the files endpoint with a file_path that resolves to a directory inside output/ or the allowed roots — e.g. requesting 'output' itself, a subfolder, or an empty path segment that resolves to a directory.","commonSituations":"Client builds the URL from a path that includes a trailing directory component; requesting a bundle/folder expecting the server to zip it; passing a prefix path where a filename was expected.","solutions":["Point file_path at a regular file, not a directory (append the concrete filename).","If you need a folder of artifacts, request each file individually or add a zip/archive endpoint.","Check the client path-building logic for missing filename or stray trailing slash.","Verify with the listing endpoint (if available) which concrete filenames exist."],"exampleFix":"// before\nconst res = await fetch(`/api/files/${folder}/`); // resolves to a directory\n// after\nconst res = await fetch(`/api/files/${folder}/frame_0001.mp4`);","handlingStrategy":"validation","validationCode":"import fs from 'fs';\nconst stat = fs.statSync(path.resolve('output', filePath));\nif (!stat.isFile()) throw new Error(`${filePath} is not a regular file`);","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Always request concrete filenames, never directory paths.","Build request URLs from file listings, not guessed folder paths.","Strip trailing slashes from client-side path construction.","Add client-side stat/existence checks where the filesystem is accessible."],"tags":["http-400","filesystem","bad-request"],"backgroundTag":"path-is-not-a-file","analyzedSha":"848b054e4fae40dabc62ec58e960b573e83793ac","analyzedAt":"2026-08-30T03:24:41.468Z","schemaVersion":2},"datasetVersion":"2026-08-30T08:17:16.595Z"}