immich-app/immich · error · PigeonError

kUnsupportedOSError

kUnsupportedOSError

Error message

This feature requires iOS 16 or later.

What it means

getMediaChanges on iOS relies on PHPhotoLibrary change-observation / sync APIs gated behind iOS 16. When running on iOS 15 or earlier, the implementation throws a PigeonError with code kUnsupportedOSError stating the feature requires iOS 16+.

Solutions

  1. Require iOS 16+ for this feature and gate the call with #available(iOS 16, *) before invoking
  2. Fall back to a full library re-sync (or the legacy sync path) on iOS 15
  3. Raise the deployment target to iOS 16 if the delta-sync feature is essential

Example fix

// before
let delta = try await api.getMediaChanges()
// after
if #available(iOS 16, *) {
  let delta = try await api.getMediaChanges()
} else {
  let delta = try await fullResync() // legacy fallback
}
Defensive patterns

Strategy: type-guard

Validate before calling

let syncAvailable: Bool {
  if #available(iOS 16, *) { return true }
  return false
}

Type guard

func supportsMediaChanges() -> Bool {
  if #available(iOS 16, *) { return true }
  return false
}

Try / catch

do {
  let delta = try await impl.getMediaChanges()
} catch let e as PigeonError where e.code == kUnsupportedOSError {
  fallBackToFullResync()
}

Prevention

When it happens

Trigger: Calling getMediaChanges on a device or simulator running iOS < 16; an OS-version check was not performed before invoking the sync delta API.

Common situations: Users on older iPhones/iPads; CI or manual testing on an iOS 15 simulator; app supporting a minimum deployment target below iOS 16 while using this API unconditionally.

Understand the failure class

Background: "unsupported platform" / "not supported on this platform" errors: what they mean and how to fix them — this error's family across 47 libraries.

Related errors


AI-assisted analysis of immich-app/immich@e55ac299a4 (2026-09-15). Data as JSON: /api/errors/12e0b47af9ec6f89. Report an issue: GitHub.

Appendix: source

Thrown at mobile/ios/Runner/Sync/MessagesImpl.swift:147

        )
        
        if let firstAsset = assets.firstObject {
          domainAlbum.updatedAt = firstAsset.modificationDate.map { Int64($0.timeIntervalSince1970) }
        }
        
        albums.append(domainAlbum)
      }
    }
    return albums.sorted { $0.id < $1.id }
  }
  
  func getMediaChanges(completion: @escaping (Result<SyncDelta, Error>) -> Void) {
    runSync(completion) { try $0.getMediaChanges() }
  }
  
  private func getMediaChanges() throws -> SyncDelta {
    guard #available(iOS 16, *) else {
      throw PigeonError(code: kUnsupportedOSError, message: "This feature requires iOS 16 or later.", details: nil)
    }
    
    guard PHPhotoLibrary.authorizationStatus(for: .readWrite) == .authorized else {
      throw PigeonError(code: "NO_AUTH", message: "No photo library access", details: nil)
    }
    
    guard let storedToken = getChangeToken() else {
      // No token exists, definitely need a full sync
      print("MediaManager::getMediaChanges: No token found")
      throw PigeonError(code: "NO_TOKEN", message: "No stored change token", details: nil)
    }
    
    let currentToken = PHPhotoLibrary.shared().currentChangeToken
    if storedToken == currentToken {
      return SyncDelta(hasChanges: false, updates: [], deletes: [], assetAlbums: [:])
    }
    
    let changes = try PHPhotoLibrary.shared().fetchPersistentChanges(since: storedToken)

View on GitHub (pinned to e55ac299a4)