{"record":{"id":"d6cb3b623718fa6f","repo":"immich-app/immich","slug":"session-not-found","errorCode":null,"errorMessage":"Session not found","messagePattern":"Session not found","errorType":"http","errorClass":"NotFoundException","httpStatus":404,"severity":"warning","filePath":"server/src/services/hls.service.ts","lineNumber":96,"sourceCode":"    const hintedSegment = position === undefined ? undefined : this.positionToSegmentIndex(segmentation, position);\n    this.prewarmVariant(assetId, sessionId, variantIndex, hintedSegment);\n\n    return this.generateMediaPlaylist(asset, segmentation);\n  }\n\n  async getSegment(\n    auth: AuthDto,\n    assetId: string,\n    sessionId: string,\n    variantIndex: number,\n    filename: string,\n    initSegment?: number,\n  ) {\n    await this.requireAccess({ auth, permission: Permission.AssetView, ids: [assetId] });\n\n    const session = await this.videoStreamRepository.getSession(sessionId);\n    if (!session) {\n      throw new NotFoundException('Session not found');\n    }\n\n    const variantDir = StorageCore.getHlsVariantFolder({ ownerId: auth.user.id, sessionId, variantIndex });\n    const path = join(variantDir, filename);\n    const response = new ImmichFileResponse({\n      path,\n      contentType: 'video/mp4',\n      cacheControl: CacheControl.PrivateWithCache,\n    });\n\n    const apiSession = this.trackSession(sessionId, variantIndex);\n    const segmentIndex = this.getSegmentIndex(apiSession, filename, initSegment);\n    this.websocketRepository.serverSend('HlsHeartbeat', { sessionId, variantIndex, segmentIndex });\n\n    if (await this.storageRepository.checkFileExists(path, constants.R_OK)) {\n      return response;\n    }\n","sourceCodeStart":78,"sourceCodeEnd":114,"githubUrl":"https://github.com/immich-app/immich/blob/199723261c6ffa897fec8ccdaea6359e39c37cc3/server/src/services/hls.service.ts#L78-L114","documentation":"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.","triggerScenarios":"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.","commonSituations":"Long-paused video whose session expired; worker restart dropping active sessions; clock skew; browser resuming playback after sleep; CDN caching a stale segment URL.","solutions":["Reload the player / re-request the main playlist to start a new session, then stream from the new sessionId.","Tune HLS session TTL if sessions expire too quickly for your usage (server config / session management).","Confirm at least one microservices worker is up so sessions persist.","Avoid hard-caching segment URLs on the client - always resolve them through the current session."],"exampleFix":null,"handlingStrategy":"retry","validationCode":"// verify session is alive before requesting segments\nconst session = await api.hlsApi.getSession(sessionId); // if available\nif (!session) {\n  const main = await api.hlsApi.getMainPlaylist(auth, assetId);\n  sessionId = parseSessionId(main);\n}","typeGuard":"const isLiveSession = (s: { id: string; closedAt?: string | null } | null): s is { id: string } =>\n  !!s && !s.closedAt;","tryCatchPattern":"try {\n  return await api.hlsApi.getSegment(auth, assetId, sessionId, variantIndex, filename);\n} catch (e) {\n  if (e.status === 404 && /Session not found/.test(e.message)) {\n    // session expired - restart playback to get a new sessionId\n    onSessionExpired();\n    return;\n  }\n  throw e;\n}","preventionTips":["Do not long-cache segment URLs - resolve through the current session.","Restart the player on session expiry instead of retrying the same URL.","Tune session TTL to match expected playback durations."],"tags":["video","hls","session","not-found","immich","nestjs","streaming"],"backgroundTag":null,"analyzedSha":"199723261c6ffa897fec8ccdaea6359e39c37cc3","analyzedAt":"2026-08-12T04:54:27.085Z","schemaVersion":2},"datasetVersion":"2026-08-12T13:17:24.610Z"}