langfuse/langfuse · error · ForbiddenError
Access denied - need to use basic auth with secret key to ${
Error message
Access denied - need to use basic auth with secret key to ${req.method} prompts What it means
ForbiddenError thrown when the auth key's scope.accessLevel is not 'project': prompt API operations require a project-scoped secret key (basic auth), not a public or organization-scoped key.
Source
Thrown at web/src/features/prompts/server/utils/authorizePromptRequest.ts:17
import { ApiAuthService } from "@/src/features/public-api/server/apiAuth";
import { type NextApiRequest } from "next";
import { UnauthorizedError, ForbiddenError } from "@langfuse/shared";
import { prisma } from "@langfuse/shared/src/db";
import {
type AuthHeaderValidVerificationResult,
redis,
} from "@langfuse/shared/src/server";
export async function authorizePromptRequestOrThrow(req: NextApiRequest) {
const authCheck = await new ApiAuthService(
prisma,
redis,
).verifyAuthHeaderAndReturnScope(req.headers.authorization);
if (!authCheck.validKey) throw new UnauthorizedError(authCheck.error);
if (authCheck.scope.accessLevel !== "project")
throw new ForbiddenError(
`Access denied - need to use basic auth with secret key to ${req.method} prompts`,
);
if (!authCheck.scope.projectId) {
throw new ForbiddenError(`No valid projectId found for auth token`);
}
return authCheck as AuthHeaderValidVerificationResult & {
scope: { projectId: string; accessLevel: "project" };
};
}
View on GitHub (pinned to 59d92c7cf3)
Solutions
- Use the project's secret key (sk-) with Basic auth for prompt endpoints
- Confirm accessLevel is 'project' in the key's settings
- Separate ingestion keys from management keys in your configuration
Example fix
// before Authorization: Basic base64(publicApiKey:) // after Authorization: Basic base64(publicKey:secretKey)
Defensive patterns
Strategy: validation
Validate before calling
// Only use project secret keys for prompt management
if (!secretKey.startsWith('sk-')) throw new Error('Prompt APIs need a project secret key'); Type guard
const isProjectSecretKey = (k: string) => k.startsWith('sk-'); Try / catch
try {
await api.prompts.create(body);
} catch (e) {
if (e.status === 403 && /basic auth with secret key/.test(e.message)) {
// switch from public/bearer to project secret key
}
} Prevention
- Separate ingestion keys from management keys
- Default prompt SDK auth to Basic with sk- secret keys
When it happens
Trigger: Calling GET/POST/etc. on /api/public/prompts with a public key (publicApiKey access level) or any non-project scope, where req.method is reflected in the message.
Common situations: Using a public-only key (e.g., tracing ingestion key) for prompt management; mixing up pk-/sk- prefixed keys; scripts built for ingestion reused for prompt CRUD.
Understand the failure class
- Authentication and authorization failures — expired tokens, bad credentials, and missing scopes.
Related errors
- No valid projectId found for auth token
- Access denied: Bearer auth and org api keys are not allowed
- Missing projectId in scope. Are you using an organization ke
- ${authCheck.error}
- Unauthorized: Cannot use organization key with bearer auth
AI-assisted analysis of langfuse/langfuse@59d92c7cf3 (2026-08-27).
Data as JSON: /api/errors/d504d195c7a2fb47.
Report an issue: GitHub.