immich-app/immich · warning
Unable to save person path without an owner
Error message
Unable to save person path without an owner
What it means
savePath persists the new path after a move. For a person-face thumbnail it requires the face's ownerId; if it is missing, the operation logs this warning and silently returns, so the person record keeps pointing at the old thumbnail path.
Solutions
- Backfill the missing ownerId on the person/face record and re-run the move
- Investigate how an ownerless person record was created and fix the import/migration path
- Clean up orphaned person rows so they are not moved at all
- Treat the warning as data-integrity signal and audit person table for null owners
Example fix
// before await storageCore.moveFile(PersonPathType.Face, personId, newPath); // ownerId missing // after await storageCore.moveFile(PersonPathType.Face, personId, newPath, ownerId: person.ownerId);
Defensive patterns
Strategy: type-guard
Validate before calling
const person = await personRepo.getById(id);
if (!person?.ownerId) throw new Error(`Person ${id} has no owner; fix data before moving`); Type guard
function hasOwner(p: { ownerId?: string | null }): p is { ownerId: string } { return typeof p.ownerId === 'string' && p.ownerId.length > 0; } Try / catch
try {
await storageCore.moveFile(PersonPathType.Face, id, newPath, ownerId);
} catch (e) {
logger.error('person move failed, keeping old path', e);
await personRepo.update({ ownerId, thumbnailPath: oldPath }); // stay consistent
} Prevention
- Audit person/face tables for null ownerId regularly
- Require ownerId in move calls via non-nullable parameters
- Fix data migrations to always link owners
- Log and alert on the 'without an owner' warning
When it happens
Trigger: moveFile -> savePath with pathType Face (PersonPathType.Face) and ownerId is null/undefined — moving a person thumbnail for a face row lacking an owner.
Common situations: Orphaned person/face rows from imports or migrations without owner linkage; data repaired manually and ownerId dropped; bugs in face-clustering pipelines creating ownerless records.
Understand the failure class
Background: "missing required argument" and "the following required arguments were not provided": what required-argument errors mean and how to fix them — this error's family across 20 libraries.
Related errors
- The database currently has
- Unexpected path type:
- Admin account does not exist
- Album must have an owner
- Asset dimensions are not available for editing
AI-assisted analysis of immich-app/immich@e55ac299a4 (2026-09-15).
Data as JSON: /api/errors/d581cd6dd6cff971.
Report an issue: GitHub.
Appendix: source
Thrown at server/src/cores/storage.core.ts:342
private savePath(pathType: PathType, id: string, newPath: string, ownerId?: string) {
switch (pathType) {
case AssetPathType.Original: {
return this.assetRepository.update({ id, originalPath: newPath });
}
case AssetFileType.FullSize:
case AssetFileType.EncodedVideo:
case AssetFileType.Thumbnail:
case AssetFileType.Preview:
case AssetFileType.Sidecar:
case AssetPathType.EncodedVideo: {
return this.assetRepository.upsertFile({ assetId: id, type: pathType as AssetFileType, path: newPath });
}
case PersonPathType.Face: {
if (!ownerId) {
this.logger.warn('Unable to save person path without an owner');
return;
}
return this.personRepository.update({ ownerId, personGroupId: id, thumbnailPath: newPath });
}
case UserPathType.Profile: {
this.logger.warn('Unexpected path type:', pathType);
return;
}
}
}
static getNestedFolder(folder: StorageFolder, ownerId: string, filename: string): string {
return join(StorageCore.getFolderLocation(folder, ownerId), filename.slice(0, 2), filename.slice(2, 4));
}
static getNestedPath(folder: StorageFolder, ownerId: string, filename: string): string {View on GitHub (pinned to e55ac299a4)