immich-app/immich · error · ForbiddenException
Forbidden
Error message
Forbidden
What it means
authenticate enforces route metadata after validating the request. If the resolved authDto.user is not an admin but the route is flagged adminRoute, access is denied with a 403 Forbidden (and a warning is logged with the denied URI). This is the admin-only route guard for the API.
Solutions
- Perform the action with an administrator account or an API key created by an admin.
- If the user should be admin, promote them via an existing admin (or server CLI) and retry.
- Change the client to use the equivalent non-admin endpoint if one exists.
Example fix
// before const apiKey = createUserApiKey(nonAdminUser, 'key'); // 403 on admin routes // after const apiKey = await adminApi.usersApi.createApiKey(adminUserId, 'admin-key');
Defensive patterns
Strategy: try-catch
Validate before calling
const me = await api.usersApi.getMyUser();
if (!me.isAdmin) {
throw new Error('Admin privileges required for this endpoint');
} Type guard
function isAdminUser(u: { isAdmin: boolean }): u is { isAdmin: true } {
return u.isAdmin === true;
} Try / catch
try {
await api.usersApi.getAllUsers(); // admin route
} catch (e) {
if (e.status === 403) {
// surface 'admin account required' to the caller
}
throw e;
} Prevention
- Check the user's isAdmin flag before calling admin endpoints.
- Create admin-scoped API keys for administrative automation.
- Handle role downgrades by refreshing stored user info.
When it happens
Trigger: A non-admin authenticated user (session, API key, or shared link holder) calls an admin-only endpoint such as user management or server-config endpoints.
Common situations: Regular users hitting admin endpoints from scripts/UIs that assume elevated rights; an API key minted by a non-admin account; role downgraded to non-admin while an old client still calls admin routes.
Understand the failure class
Background: Permission denied / not authorized / 403 Forbidden: access-control rejections when the caller lacks the required role, grant, or ownership — this error's family across 18 libraries.
- Authentication and authorization failures — expired tokens, bad credentials, and missing scopes.
Related errors
- Admin setup is not available
- Denied access to admin only route
- Denied access to non-shared route
- Elevated permission is required
- error instanceof Error ? error.message : error
AI-assisted analysis of immich-app/immich@f48d4b3321 (2026-09-15).
Data as JSON: /api/errors/a7d2ca0012586bf1.
Report an issue: GitHub.
Appendix: source
Thrown at server/src/services/auth.service.ts:222
const admin = await this.createUser({
isAdmin: true,
email: dto.email,
name: dto.name,
password: dto.password,
storageLabel: 'admin',
});
return mapUserAdmin(admin);
}
async authenticate({ headers, queryParams, metadata }: ValidateRequest): Promise<AuthDto> {
const authDto = await this.validate({ headers, queryParams });
const { adminRoute, sharedLinkRoute, uri } = metadata;
const requestedPermission = metadata.permission ?? Permission.All;
if (!authDto.user.isAdmin && adminRoute) {
this.logger.warn(`Denied access to admin only route: ${uri}`);
throw new ForbiddenException('Forbidden');
}
if (authDto.sharedLink && !sharedLinkRoute) {
this.logger.warn(`Denied access to non-shared route: ${uri}`);
throw new ForbiddenException('Forbidden');
}
if (
authDto.apiKey &&
requestedPermission !== false &&
!isGranted({ requested: [requestedPermission], current: authDto.apiKey.permissions })
) {
throw new ForbiddenException(`Missing required permission: ${requestedPermission}`);
}
return authDto;
}
View on GitHub (pinned to f48d4b3321)