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
- Downscale the bitmap before rotation so w*h*4 native allocation succeeds.
- Serialize/limit concurrent native rotations to reduce peak native memory.
- Catch the IOException and fall back to a non-rotated (or Matrix-based) rendering for the asset.
- 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
- Downscale huge images before native rotation.
- Limit concurrent native image operations.
- Sanity-check orientation values before calling native code.
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
- could not convert bitmap to ARGB_8888
- Could not read image data for $assetId
- Asset does not have valid dimensions
- Cannot open original stream for asset $assetId
- Cannot unlink Android motion photos
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)