immich-app/immich · error · BadRequestException
Cannot rotate an API Key with permissions you do not have
Error message
Cannot rotate an API Key with permissions you do not have
What it means
Raised by ApiKeyService.rotate when rotation is requested while authenticated with an API key whose permissions do not cover the (possibly renewed) permission set. Like create, it enforces that a key can never gain privileges it did not already have; the input at fault is dto.permissions (or the stored permission set) compared against auth.apiKey.permissions via isGranted.
Solutions
- Rotate without expanding permissions, keeping them within the authenticating key's grant
- Perform the rotation authenticated as the owning user rather than through the limited API key
Defensive patterns
Strategy: validation
When it happens
Trigger: Thrown at server/src/services/api-key.service.ts:57 when the library encounters an invalid state.
Common situations: See trigger scenarios.
AI-assisted analysis of immich-app/immich@e55ac299a4 (2026-09-15).
Data as JSON: /api/errors/29550e8d5ef2b2c8.
Report an issue: GitHub.
Appendix: source
Thrown at server/src/services/api-key.service.ts:57
dto.permissions &&
!isGranted({ requested: dto.permissions, current: auth.apiKey.permissions })
) {
throw new BadRequestException('Cannot grant permissions you do not have');
}
const key = await this.apiKeyRepository.update(auth.user.id, id, { name: dto.name, permissions: dto.permissions });
return this.map(key);
}
async rotate(auth: AuthDto, id: string): Promise<ApiKeyCreateResponseDto> {
const existing = await findOrFail(() => this.apiKeyRepository.getById(auth.user.id, id), 'API Key not found');
if (
auth.apiKey &&
!isGranted({ requested: existing.permissions as Permission[], current: auth.apiKey.permissions })
) {
throw new BadRequestException('Cannot rotate an API Key with permissions you do not have');
}
const token = this.cryptoRepository.randomBytesAsText(32);
const hashed = this.cryptoRepository.hashSha256(token);
const newKey = await this.apiKeyRepository.update(auth.user.id, id, { key: hashed });
const apiKey = this.map(newKey);
return { ...apiKey, secret: token, apiKey };
}
async delete(auth: AuthDto, id: string): Promise<void> {
const exists = await this.apiKeyRepository.getById(auth.user.id, id);
if (!exists) {
throw new BadRequestException('API Key not found');
}
await this.apiKeyRepository.delete(auth.user.id, id);
}View on GitHub (pinned to e55ac299a4)