{"record":{"id":"4f32b20b5fdceb33","repo":"langfuse/langfuse","slug":"forbidden-4f32b2","errorCode":null,"errorMessage":"Forbidden","messagePattern":"Forbidden","errorType":"exception","errorClass":"ForbiddenError","httpStatus":403,"severity":"error","filePath":"web/src/pages/api/public/media/[mediaId].ts","lineNumber":22,"sourceCode":"  getMedia,\n  updateMediaUploadStatus,\n} from \"@/src/features/media/server/mediaService\";\nimport {\n  GetMediaQuerySchema,\n  GetMediaResponseSchema,\n  PatchMediaBodySchema,\n} from \"@/src/features/media/validation\";\nimport { createAuthedProjectAPIRoute } from \"@/src/features/public-api/server/createAuthedProjectAPIRoute\";\nimport { withMiddlewares } from \"@/src/features/public-api/server/withMiddlewares\";\nimport { ForbiddenError } from \"@langfuse/shared\";\n\nexport default withMiddlewares({\n  GET: createAuthedProjectAPIRoute({\n    name: \"Get Media data\",\n    querySchema: GetMediaQuerySchema,\n    responseSchema: GetMediaResponseSchema,\n    fn: async ({ query, auth }) => {\n      if (auth.scope.accessLevel !== \"project\") throw new ForbiddenError();\n\n      const { projectId } = auth.scope;\n      const { mediaId } = query;\n\n      return await getMedia({ projectId, mediaId });\n    },\n  }),\n\n  PATCH: createAuthedProjectAPIRoute({\n    name: \"Update Media Uploaded At\",\n    querySchema: z.object({\n      mediaId: z.string(),\n    }),\n    bodySchema: PatchMediaBodySchema,\n    responseSchema: z.void(),\n    rateLimitResource: \"media-upload\",\n    fn: async ({ query, body, auth }) => {\n      if (auth.scope.accessLevel !== \"project\") throw new ForbiddenError();","sourceCodeStart":4,"sourceCodeEnd":40,"githubUrl":"https://github.com/langfuse/langfuse/blob/59d92c7cf365150d10b753b5a0d1708902a2ed60/web/src/pages/api/public/media/[mediaId].ts#L4-L40","documentation":"GET /api/public/media/:mediaId requires a project-scoped API key; requests authenticated with org-level keys or Bearer PATs get a bare 403 Forbidden before the media lookup.","triggerScenarios":"Calling the media GET endpoint with an organization API key or Bearer token instead of a project pk/sk pair.","commonSituations":"Scripts that reuse one org key for all public API calls; PAT-based automation hitting project-only endpoints.","solutions":["Use a project-scoped API key with Basic auth for media endpoints"],"exampleFix":null,"handlingStrategy":"validation","validationCode":"if (publicKey.startsWith('org-')) throw new Error('media endpoints need a project key');","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Use dedicated project keys for ingestion/media flows"],"tags":["authorization","media","scope"],"backgroundTag":"insufficient-api-key-scope","analyzedSha":"59d92c7cf365150d10b753b5a0d1708902a2ed60","analyzedAt":"2026-08-27T22:22:00.402Z","schemaVersion":2},"datasetVersion":"2026-08-28T00:17:15.603Z"}