{"record":{"id":"7db9cfe3ad1dd44f","repo":"langfuse/langfuse","slug":"method-not-allowed-7db9cf","errorCode":null,"errorMessage":"Method Not Allowed","messagePattern":"Method Not Allowed","errorType":"http","errorClass":null,"httpStatus":405,"severity":"error","filePath":"web/src/pages/api/public/projects/[projectId]/apiKeys/index.ts","lineNumber":22,"sourceCode":"import { ApiAuthService } from \"@/src/features/public-api/server/apiAuth\";\nimport { cors, runMiddleware } from \"@/src/features/public-api/server/cors\";\nimport { RateLimitService } from \"@/src/features/public-api/server/RateLimitService\";\nimport {\n  validateQueryAndExtractId,\n  handleGetApiKeys,\n  handleCreateApiKey,\n} from \"@/src/ee/features/admin-api/server/projects/projectById/apiKeys\";\nimport { hasEntitlementBasedOnPlan } from \"@/src/features/entitlements/server/hasEntitlement\";\n\nexport default async function handler(\n  req: NextApiRequest,\n  res: NextApiResponse,\n) {\n  await runMiddleware(req, res, cors);\n\n  try {\n    if (req.method !== \"POST\" && req.method !== \"GET\") {\n      res.status(405).json({ message: \"Method Not Allowed\" });\n      return;\n    }\n\n    // CHECK AUTH\n    const authCheck = await new ApiAuthService(\n      prisma,\n      redis,\n    ).verifyAuthHeaderAndReturnScope(req.headers.authorization);\n    if (!authCheck.validKey) {\n      return res.status(401).json({\n        message: authCheck.error,\n      });\n    }\n\n    // Check if using an organization API key\n    if (\n      authCheck.scope.accessLevel !== \"organization\" ||\n      !authCheck.scope.orgId","sourceCodeStart":4,"sourceCodeEnd":40,"githubUrl":"https://github.com/langfuse/langfuse/blob/59d92c7cf365150d10b753b5a0d1708902a2ed60/web/src/pages/api/public/projects/[projectId]/apiKeys/index.ts#L4-L40","documentation":"The project API key collection endpoint (/api/public/projects/{projectId}/apiKeys) only accepts POST (create key) and GET (list keys). Any other HTTP verb is rejected with 405 before auth runs.","triggerScenarios":"Sending PUT, PATCH, DELETE, or OPTIONS-without-CORS to /api/public/projects/{projectId}/apiKeys. For example, trying to delete a key via the collection URL instead of the item URL.","commonSituations":"Misguided REST attempts to modify or delete via the collection route, stale generated clients, or load-balancer health checks using unexpected methods.","solutions":["Use GET to list keys and POST to create keys on this route.","Use DELETE /api/public/projects/{projectId}/apiKeys/{apiKeyId} to delete a specific key."],"exampleFix":"// before\nfetch(`${baseUrl}/api/public/projects/${projectId}/apiKeys`, { method: 'DELETE' })\n// after\nfetch(`${baseUrl}/api/public/projects/${projectId}/apiKeys/${encodeURIComponent(apiKeyId)}`, { method: 'DELETE' })","handlingStrategy":"type-guard","validationCode":"const allowed = ['GET', 'POST'];\nif (!allowed.includes(method)) throw new Error(`Method ${method} not allowed; use GET or POST`);","typeGuard":"function isSupportedKeyCollectionMethod(m: string): m is 'GET' | 'POST' {\n  return m === 'GET' || m === 'POST';\n}","tryCatchPattern":null,"preventionTips":["Delete keys via the item route (DELETE .../apiKeys/{id}), never the collection route."],"tags":["http-405","method-not-allowed","admin-api","rest"],"backgroundTag":"http-method-not-allowed","analyzedSha":"59d92c7cf365150d10b753b5a0d1708902a2ed60","analyzedAt":"2026-08-27T22:22:00.402Z","schemaVersion":2},"datasetVersion":"2026-08-28T00:17:15.603Z"}