immich-app/immich · error · ForbiddenException

Sync endpoints cannot be used with API keys

Error message

Sync endpoints cannot be used with API keys

What it means

The sync endpoints require an authenticated session (created via password login), not a machine-learning/API-key auth. throwSessionRequired raises ForbiddenException when the AuthDto has no session attached, because sync checkpoints/acks are scoped to a session.

Solutions

  1. Authenticate with username/password login to obtain a session instead of an API key
  2. Use the Immich mobile app or a session-based client for sync operations
  3. If scripting is required, log in via POST /api/auth/login and use the returned session token

Example fix

// before
headers: { 'x-api-key': apiKey }
call GET /api/sync/acks
// after
const { accessToken } = await login(email, password)
headers: { Authorization: 'Bearer ' + accessToken }
call GET /api/sync/acks
Defensive patterns

Strategy: validation

Validate before calling

const isSyncCapable = (auth) => Boolean(auth.session?.id);
if (!isSyncCapable(auth)) throw new Error('Sync requires a login session, not an API key');

Type guard

function hasSession(auth) { return typeof auth === 'object' && auth !== null && 'session' in auth && auth.session != null && typeof auth.session.id === 'string'; }

Try / catch

try {
  await syncClient.getAcks();
} catch (e) {
  if (e.status === 403) { await loginWithPassword(); /* retry with session */ }
}

Prevention

When it happens

Trigger: Calling any sync endpoint (getAcks, setAcks, deleteAcks, streamInternal) with an AuthDto whose auth.session is undefined — i.e. authenticated via API key or another non-session auth type.

Common situations: Scripts using an API key to call /api/sync endpoints; mobile-client-only sync API invoked from automation; using API keys generated for external tools.

Related errors


AI-assisted analysis of immich-app/immich@f48d4b3321 (2026-09-15). Data as JSON: /api/errors/d63c8a88ceabe569. Report an issue: GitHub.

Appendix: source

Thrown at server/src/services/sync.service.ts:97

  SyncRequestType.AlbumUsersV1,
  SyncRequestType.AlbumToAssetsV1,
  SyncRequestType.AssetExifsV1,
  SyncRequestType.AlbumAssetExifsV1,
  SyncRequestType.AssetOcrV1,
  SyncRequestType.PartnerAssetExifsV1,
  SyncRequestType.MemoriesV1,
  SyncRequestType.MemoryToAssetsV1,
  SyncRequestType.PeopleV1,
  SyncRequestType.AssetFacesV1,
  SyncRequestType.AssetFacesV2,
  SyncRequestType.AssetFacesV3,
  SyncRequestType.UserMetadataV1,
  SyncRequestType.AssetMetadataV1,
  SyncRequestType.AssetEditsV1,
];

const throwSessionRequired = () => {
  throw new ForbiddenException('Sync endpoints cannot be used with API keys');
};

@Injectable()
export class SyncService extends BaseService {
  getAcks(auth: AuthDto) {
    const sessionId = auth.session?.id;
    if (!sessionId) {
      return throwSessionRequired();
    }

    return this.syncCheckpointRepository.getAll(sessionId);
  }

  async setAcks(auth: AuthDto, dto: SyncAckSetDto) {
    const sessionId = auth.session?.id;
    if (!sessionId) {
      return throwSessionRequired();
    }

View on GitHub (pinned to f48d4b3321)