gitroomhq/postiz-app · error · HttpException
Unauthorized
Error message
Unauthorized
What it means
The admin controller's assertSuperAdmin guard throws HttpException('Unauthorized', 400) when the authenticated user lacks the isSuperAdmin flag. It protects all super-admin-only endpoints (listErrors, listPlatforms, getStats). Note the status code is 400 (Bad Request) even though the message says Unauthorized, which is misleading.
Source
Thrown at apps/backend/src/api/routes/admin.controller.ts:24
} from '@nestjs/common';
import { GetUserFromRequest } from '@gitroom/nestjs-libraries/user/user.from.request';
import { User } from '@prisma/client';
import { ApiTags } from '@nestjs/swagger';
import { ErrorsService } from '@gitroom/nestjs-libraries/database/prisma/errors/errors.service';
import { AdminStatsService } from '@gitroom/nestjs-libraries/database/prisma/admin-stats/admin-stats.service';
import dayjs from 'dayjs';
@ApiTags('Admin')
@Controller('/admin')
export class AdminController {
constructor(
private _errorsService: ErrorsService,
private _adminStatsService: AdminStatsService
) {}
private assertSuperAdmin(user: User) {
if (!user?.isSuperAdmin) {
throw new HttpException('Unauthorized', 400);
}
}
@Get('/errors')
async listErrors(
@GetUserFromRequest() user: User,
@Query('page') page?: string,
@Query('limit') limit?: string,
@Query('platform') platform?: string,
@Query('email') email?: string,
@Query('unknownFirst') unknownFirst?: string
) {
this.assertSuperAdmin(user);
return this._errorsService.listErrors({
page: page ? parseInt(page, 10) : 0,
limit: limit ? parseInt(limit, 10) : 20,
platform: platform || undefined,
email: email || undefined,View on GitHub (pinned to 0f1647f749)
Solutions
- Verify the user row in the database actually has isSuperAdmin=true (User table) and re-login
- For self-hosted setups, set the IS_SUPER_ADMIN env variable to your email/ID before registering so the first user is promoted
- If you are the admin but still blocked, clear cookies/session and re-authenticate so GetUserFromRequest resolves the full user
- If you maintain the code, consider throwing 401/403 instead of 400 for semantic correctness
Example fix
// before
if (!user?.isSuperAdmin) {
throw new HttpException('Unauthorized', 400);
}
// after
if (!user?.isSuperAdmin) {
throw new HttpException('Unauthorized', HttpStatus.FORBIDDEN);
} Defensive patterns
Strategy: type-guard
Validate before calling
const me = await api.getMe();
if (!me?.isSuperAdmin) {
throw new Error('This action requires a super admin account');
} Type guard
const isSuperAdmin = (u: User | null | undefined): u is User & { isSuperAdmin: true } =>
Boolean(u?.isSuperAdmin); Try / catch
try { await adminApi.getStats(); } catch (e) { if (e instanceof HttpException && e.message === 'Unauthorized') showPermissionError(); else throw e; } Prevention
- Gate admin UI routes behind an isSuperAdmin check before rendering
- Fetch /me on app boot and store the role for route guards
- Never cache admin credentials across role downgrades; force re-login
When it happens
Trigger: Calling GET /admin/errors, /admin/platforms or /admin/stats with a session/user whose isSuperAdmin property is false or undefined. Happens for regular org users, expired sessions that resolve to a partial user object, or self-hosted instances where no super admin was configured.
Common situations: Self-hosting Postiz without setting IS_SUPER_ADMIN for the first user; logging in with a normal account and navigating to admin routes; token from a user record where isSuperAdmin was never set in the database.
Understand the failure class
- Authentication and authorization failures — expired tokens, bad credentials, and missing scopes.
Related errors
- Unauthorized
- Unauthorized
- Subscription required: section ${item[1]}, action ${item[0]}
- Invalid redirect_uri
- code_challenge is required for this client
AI-assisted analysis of gitroomhq/postiz-app@0f1647f749 (2026-08-27).
Data as JSON: /api/errors/5860fe16b79997c6.
Report an issue: GitHub.