immich-app/immich · warning · NotFoundException

Session not found

Error message

Session not found

What it means

Each HLS segment request is tied to a session row created when the main playlist was generated. getSegment looks up that session by id via videoStreamRepository.getSession(sessionId); a null means the session was never created, was already cleaned up (expiry/close), or the id is wrong. Returns 404 NotFoundException. Sessions are also tracked in-memory via trackSession, but the DB lookup is authoritative.

Source

Thrown at server/src/services/hls.service.ts:96

    const hintedSegment = position === undefined ? undefined : this.positionToSegmentIndex(segmentation, position);
    this.prewarmVariant(assetId, sessionId, variantIndex, hintedSegment);

    return this.generateMediaPlaylist(asset, segmentation);
  }

  async getSegment(
    auth: AuthDto,
    assetId: string,
    sessionId: string,
    variantIndex: number,
    filename: string,
    initSegment?: number,
  ) {
    await this.requireAccess({ auth, permission: Permission.AssetView, ids: [assetId] });

    const session = await this.videoStreamRepository.getSession(sessionId);
    if (!session) {
      throw new NotFoundException('Session not found');
    }

    const variantDir = StorageCore.getHlsVariantFolder({ ownerId: auth.user.id, sessionId, variantIndex });
    const path = join(variantDir, filename);
    const response = new ImmichFileResponse({
      path,
      contentType: 'video/mp4',
      cacheControl: CacheControl.PrivateWithCache,
    });

    const apiSession = this.trackSession(sessionId, variantIndex);
    const segmentIndex = this.getSegmentIndex(apiSession, filename, initSegment);
    this.websocketRepository.serverSend('HlsHeartbeat', { sessionId, variantIndex, segmentIndex });

    if (await this.storageRepository.checkFileExists(path, constants.R_OK)) {
      return response;
    }

View on GitHub (pinned to 199723261c)

Solutions

  1. Reload the player / re-request the main playlist to start a new session, then stream from the new sessionId.
  2. Tune HLS session TTL if sessions expire too quickly for your usage (server config / session management).
  3. Confirm at least one microservices worker is up so sessions persist.
  4. Avoid hard-caching segment URLs on the client - always resolve them through the current session.
Defensive patterns

Strategy: retry

Validate before calling

// verify session is alive before requesting segments
const session = await api.hlsApi.getSession(sessionId); // if available
if (!session) {
  const main = await api.hlsApi.getMainPlaylist(auth, assetId);
  sessionId = parseSessionId(main);
}

Type guard

const isLiveSession = (s: { id: string; closedAt?: string | null } | null): s is { id: string } =>
  !!s && !s.closedAt;

Try / catch

try {
  return await api.hlsApi.getSegment(auth, assetId, sessionId, variantIndex, filename);
} catch (e) {
  if (e.status === 404 && /Session not found/.test(e.message)) {
    // session expired - restart playback to get a new sessionId
    onSessionExpired();
    return;
  }
  throw e;
}

Prevention

When it happens

Trigger: GET /assets/{id}/video/{sessionId}/{variantIndex}/{filename}.m4s with a sessionId that does not exist in the session table. Happens when the player requests segments after the session timed out, after the worker that owned the session restarted, or with a hand-copied URL.

Common situations: Long-paused video whose session expired; worker restart dropping active sessions; clock skew; browser resuming playback after sleep; CDN caching a stale segment URL.

Related errors


AI-assisted analysis of immich-app/immich@199723261c (2026-08-12). Data as JSON: /api/errors/d6cb3b623718fa6f. Report an issue: GitHub.