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
- 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.
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
- 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.
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
- Asset not found or metadata not yet ready for streaming
- Asset metadata is not yet ready for streaming
- No supported variants for this video
- Real-time transcoding is not enabled
- Asset not found or asset is not a video
AI-assisted analysis of immich-app/immich@199723261c (2026-08-12).
Data as JSON: /api/errors/d6cb3b623718fa6f.
Report an issue: GitHub.