{"record":{"id":"d581cd6dd6cff971","repo":"immich-app/immich","slug":"unable-to-save-person-path-without-an-owner","errorCode":null,"errorMessage":"Unable to save person path without an owner","messagePattern":"Unable to save person path without an owner","errorType":"console","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"server/src/cores/storage.core.ts","lineNumber":342,"sourceCode":"\n  private savePath(pathType: PathType, id: string, newPath: string, ownerId?: string) {\n    switch (pathType) {\n      case AssetPathType.Original: {\n        return this.assetRepository.update({ id, originalPath: newPath });\n      }\n\n      case AssetFileType.FullSize:\n      case AssetFileType.EncodedVideo:\n      case AssetFileType.Thumbnail:\n      case AssetFileType.Preview:\n      case AssetFileType.Sidecar:\n      case AssetPathType.EncodedVideo: {\n        return this.assetRepository.upsertFile({ assetId: id, type: pathType as AssetFileType, path: newPath });\n      }\n\n      case PersonPathType.Face: {\n        if (!ownerId) {\n          this.logger.warn('Unable to save person path without an owner');\n          return;\n        }\n\n        return this.personRepository.update({ ownerId, personGroupId: id, thumbnailPath: newPath });\n      }\n\n      case UserPathType.Profile: {\n        this.logger.warn('Unexpected path type:', pathType);\n        return;\n      }\n    }\n  }\n\n  static getNestedFolder(folder: StorageFolder, ownerId: string, filename: string): string {\n    return join(StorageCore.getFolderLocation(folder, ownerId), filename.slice(0, 2), filename.slice(2, 4));\n  }\n\n  static getNestedPath(folder: StorageFolder, ownerId: string, filename: string): string {","sourceCodeStart":324,"sourceCodeEnd":360,"githubUrl":"https://github.com/immich-app/immich/blob/e55ac299a4ec7cb372e35dbf2c6c05ee9ce77f6c/server/src/cores/storage.core.ts#L324-L360","documentation":"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.","triggerScenarios":"moveFile -> savePath with pathType Face (PersonPathType.Face) and ownerId is null/undefined — moving a person thumbnail for a face row lacking an owner.","commonSituations":"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.","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"],"exampleFix":"// before\nawait storageCore.moveFile(PersonPathType.Face, personId, newPath); // ownerId missing\n// after\nawait storageCore.moveFile(PersonPathType.Face, personId, newPath, ownerId: person.ownerId);","handlingStrategy":"type-guard","validationCode":"const person = await personRepo.getById(id);\nif (!person?.ownerId) throw new Error(`Person ${id} has no owner; fix data before moving`);","typeGuard":"function hasOwner(p: { ownerId?: string | null }): p is { ownerId: string } { return typeof p.ownerId === 'string' && p.ownerId.length > 0; }","tryCatchPattern":"try {\n  await storageCore.moveFile(PersonPathType.Face, id, newPath, ownerId);\n} catch (e) {\n  logger.error('person move failed, keeping old path', e);\n  await personRepo.update({ ownerId, thumbnailPath: oldPath }); // stay consistent\n}","preventionTips":["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"],"tags":["data-integrity","person-faces","database","immich"],"backgroundTag":"missing-required-argument","analyzedSha":"e55ac299a4ec7cb372e35dbf2c6c05ee9ce77f6c","analyzedAt":"2026-09-15T07:20:19.675Z","contentChangedAt":"2026-09-15T07:20:19.675Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}