{"record":{"id":"9dbe5434d827e48b","repo":"unslothai/unsloth","slug":"api-endpoint-not-found","errorCode":null,"errorMessage":"API endpoint not found","messagePattern":"API endpoint not found","errorType":"http","errorClass":"HTTPException","httpStatus":404,"severity":"warning","filePath":"studio/backend/main.py","lineNumber":2354,"sourceCode":"            headers[_CSP_SCRIPT_NONCE_HEADER] = nonce\n        return Response(\n            content = content,\n            media_type = \"text/html\",\n            headers = headers,\n        )\n\n    @app.get(\"/\")\n    async def serve_root(request: Request):\n        if not _frontend_request_allowed(request):\n            return Response(status_code = 404)\n        return _build_index_response(request)\n\n    @app.get(\"/{full_path:path}\")\n    async def serve_frontend(request: Request, full_path: str):\n        # Unknown API paths: raise a real 404 so the api_errors handlers render the right envelope\n        # for /v1/* ({\"detail\": ...} for /api/*). The request path is \"/\" + full_path.\n        if full_path in {\"api\", \"v1\"} or full_path.startswith((\"api/\", \"v1/\")):\n            raise HTTPException(status_code = 404, detail = \"API endpoint not found\")\n        if not _frontend_request_allowed(request):\n            return Response(status_code = 404)\n\n        file_path = (build_path / full_path).resolve()\n\n        # Block path traversal — resolved path must stay inside build_path\n        if not file_path.is_relative_to(build_path.resolve()):\n            return Response(status_code = 403)\n\n        if file_path.is_file():\n            return FileResponse(file_path)\n\n        # Serve index.html as bytes — avoids Content-Length mismatch\n        return _build_index_response(request)\n\n    return True\n","sourceCodeStart":2336,"sourceCodeEnd":2371,"githubUrl":"https://github.com/unslothai/unsloth/blob/203007d19051dcd2ae33876786d117c99f6b0368/studio/backend/main.py#L2336-L2371","documentation":"HTTP 404 raised by the SPA catch-all route serve_frontend when the request path starts with api/ or v1/ (or is exactly 'api'/'v1') but matches no registered API route. The handler deliberately raises a real HTTPException so the api_errors handlers render the standard JSON error envelope instead of falling through to index.html.","triggerScenarios":"Requesting an unknown or removed API endpoint such as GET /api/nonexistent or POST /v1/chat/completions when that route is not registered (typo in the path, wrong HTTP method for a differently-named route, or a route that only exists in another deployment).","commonSituations":"Client/server version skew where the frontend calls an endpoint added in a newer backend; typos in integrations scripts; hitting the REST surface expecting OpenAI-compatible /v1 routes that are disabled in this build.","solutions":["Check the exact path and method against the backend's OpenAPI schema at /docs or /openapi.json.","Fix typos or version the path correctly (e.g. /api/studio/... vs /v1/...).","If the route should exist, verify the module that registers it is enabled in this build/deployment.","For non-API paths, drop the api/ or v1/ prefix — those are reserved for the API surface."],"exampleFix":null,"handlingStrategy":"type-guard","validationCode":null,"typeGuard":null,"tryCatchPattern":"async def call_api(session, path: str):\n    r = await session.get(path)\n    if r.status_code == 404:\n        detail = r.json().get(\"detail\", \"\")\n        if detail == \"API endpoint not found\":\n            raise UnknownEndpointError(path)  # client bug: path is wrong\n    r.raise_for_status()","preventionTips":["Derive API paths from the backend's /openapi.json instead of hardcoding them.","Pin client and backend versions together in deployments to avoid route skew.","Treat this specific 404 detail as 'wrong path', distinct from a missing SPA route."],"tags":["api","http-404","routing","spa"],"backgroundTag":null,"analyzedSha":"203007d19051dcd2ae33876786d117c99f6b0368","analyzedAt":"2026-08-15T02:48:39.846Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}