immich-app/immich · error · IOException

native rotate failed for orientation $orientation

Error message

native rotate failed for orientation $orientation

What it means

rotateToNativeBuffer calls NativeImage.rotate to rotate a bitmap natively for a given EXIF orientation. A returned pointer of 0 signals native allocation/rotation failure, surfaced as IOException 'native rotate failed for orientation $orientation'.

Solutions

  1. Downscale the bitmap before rotation so w*h*4 native allocation succeeds.
  2. Serialize/limit concurrent native rotations to reduce peak native memory.
  3. Catch the IOException and fall back to a non-rotated (or Matrix-based) rendering for the asset.
  4. Validate the orientation value against supported EXIF orientations before calling.

Example fix

// before
val out = rotateToNativeBuffer(bitmap, orientation)
// after
val out = try {
  rotateToNativeBuffer(bitmap, orientation)
} catch (e: IOException) {
  fallbackWithoutRotation(bitmap, orientation) // or downscaled retry
}
Defensive patterns

Strategy: try-catch

Validate before calling

// Kotlin: preflight native allocation size
val needed = bitmap.width.toLong() * bitmap.height * 4
if (needed > MAX_NATIVE_ALLOC) downsampleFirst()

Try / catch

try {
  val meta = rotateToNativeBuffer(bitmap, orientation)
} catch (e: IOException) {
  fallbackToMatrixRotate(bitmap, orientation)
}

Prevention

When it happens

Trigger: Native rotate returning nullptr because the ARGB_8888 buffer could not be locked or w*h*4 bytes could not be allocated (huge image, memory pressure), or an unsupported orientation value.

Common situations: Very large RAW photos on memory-constrained devices; corrupted orientation metadata; multiple simultaneous rotations exhausting native heap.

Understand the failure class

Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — 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/8bc789329d3d2ece. Report an issue: GitHub.

Appendix: source

Thrown at mobile/android/app/src/main/kotlin/app/alextran/immich/images/RawOrientation.kt:58

  } catch (_: Exception) {
    return ExifInterface.ORIENTATION_NORMAL
  }
}

fun Bitmap.toArgb8888(): Bitmap {
  if (config == Bitmap.Config.ARGB_8888) return this
  val converted = copy(Bitmap.Config.ARGB_8888, false)
  recycle()
  return converted ?: throw IOException("could not convert bitmap to ARGB_8888")
}

// Force ARGB_8888 first: the native rotate needs a lockable 8888 buffer and allocates w*h*4.
fun rotateToNativeBuffer(bitmap: Bitmap, orientation: Int): Map<String, Long> {
  val src = bitmap.toArgb8888()
  try {
    val info = IntArray(3)
    val pointer = NativeImage.rotate(src, orientation, info)
    if (pointer == 0L) throw IOException("native rotate failed for orientation $orientation")
    return mapOf(
      "pointer" to pointer,
      "width" to info[0].toLong(),
      "height" to info[1].toLong(),
      "rowBytes" to info[2].toLong()
    )
  } finally {
    if (!src.isRecycled) src.recycle()
  }
}

View on GitHub (pinned to e55ac299a4)