immich-app/immich · error · BadRequestException
This endpoint can only be used with a session token
Error message
This endpoint can only be used with a session token
What it means
SessionService.create() mints a new (child) session token derived from the caller's existing session. It requires auth.session to be populated, which only happens when the request was authenticated with a session JWT — not an API key. A BadRequestException (HTTP 400) is thrown at session.service.ts:32 when auth.session is absent.
Source
Thrown at server/src/services/session.service.ts:32
import { BaseService } from 'src/services/base.service';
@Injectable()
export class SessionService extends BaseService {
@OnJob({ name: JobName.SessionCleanup, queue: QueueName.BackgroundTask })
async handleCleanup(): Promise<JobStatus> {
const sessions = await this.sessionRepository.cleanup();
for (const session of sessions) {
this.logger.verbose(`Deleted expired session token: ${session.deviceOS}/${session.deviceType}`);
}
this.logger.log(`Deleted ${sessions.length} expired session tokens`);
return JobStatus.Success;
}
async create(auth: AuthDto, dto: SessionCreateDto): Promise<SessionCreateResponseDto> {
if (!auth.session) {
throw new BadRequestException('This endpoint can only be used with a session token');
}
const token = this.cryptoRepository.randomBytesAsText(32);
const hashed = this.cryptoRepository.hashSha256(token);
const session = await this.sessionRepository.create({
parentId: auth.session.id,
userId: auth.user.id,
expiresAt: dto.duration ? DateTime.now().plus({ seconds: dto.duration }).toJSDate() : null,
deviceType: dto.deviceType,
deviceOS: dto.deviceOS,
token: hashed,
});
return { ...mapSession(session), token };
}
async getAll(auth: AuthDto): Promise<SessionResponseDto[]> {
const sessions = await this.sessionRepository.getByUserId(auth.user.id);View on GitHub (pinned to 199723261c)
Solutions
- Authenticate the request with a valid session JWT (cookie or Authorization: Bearer) instead of an API key.
- If programmatic long-lived access is needed, use API keys directly rather than creating child sessions.
- Confirm the session token hasn't expired by calling GET /sessions before creating a child.
- Regenerate/refresh the session token if it is malformed or stale.
Example fix
// before - API key cannot create a session
fetch('/sessions', { headers: { 'x-api-key': apiKey } });
// after - session token required
fetch('/sessions', { headers: { Authorization: `Bearer ${sessionToken}` } }); Defensive patterns
Strategy: validation
Validate before calling
// Only create a child session when authenticated with a session token.
if (!hasSessionToken(authContext)) {
throw new Error('Use a session token, not an API key, to create a session.');
}
await sessionApi.create(authContext, dto);
function hasSessionToken(auth: AuthDto): boolean {
return !!auth?.session;
} Type guard
const hasSession = (a: AuthDto | undefined): a is AuthDto & { session: NonNullable<AuthDto['session']> } =>
!!a && !!a.session; Try / catch
try {
await sessionApi.create(auth, dto);
} catch (e) {
if (e instanceof BadRequestException && /session token/i.test(e.message)) {
redirectToLogin();
} else throw e;
} Prevention
- Do not use API keys for session-creation endpoints.
- Detect API-key vs session auth on the client and route accordingly.
- Refresh the session token before it expires.
When it happens
Trigger: POST /sessions (session creation) authenticated with an API key (auth.session is null for API-key requests) or with no/invalid session token. The endpoint cannot derive a parentId without an existing session.
Common situations: Scripts or integrations using API keys to programmatically create sessions, calling the endpoint with a bearer token that expired or was malformed, or migrating an integration that assumed API-key auth covers session creation.
Related errors
- Sync endpoints cannot be used with API keys
- Error backchannel logout: token validation failed
- Invalid logout token: it must contain either a sub or a sid
- Wrong password
- Unauthorized
AI-assisted analysis of immich-app/immich@199723261c (2026-08-12).
Data as JSON: /api/errors/f3632765fd145228.
Report an issue: GitHub.