{"record":{"id":"3e697d95c3660956","repo":"immich-app/immich","slug":"could-not-convert-bitmap-to-argb-8888","errorCode":null,"errorMessage":"could not convert bitmap to ARGB_8888","messagePattern":"could not convert bitmap to ARGB_8888","errorType":"exception","errorClass":"IOException","httpStatus":null,"severity":"error","filePath":"mobile/android/app/src/main/kotlin/app/alextran/immich/images/RawOrientation.kt","lineNumber":49,"sourceCode":"    for (i in 0 until entries) {\n      val p = start + 2 + i * 12\n      if (p + 12 > len) return ExifInterface.ORIENTATION_NORMAL\n      if ((buffer.getShort(p).toInt() and 0xFFFF) == 0x0112) {\n        val value = buffer.getShort(p + 8).toInt() and 0xFFFF\n        return if (value in 1..8) value else ExifInterface.ORIENTATION_NORMAL\n      }\n    }\n    return ExifInterface.ORIENTATION_NORMAL\n  } catch (_: Exception) {\n    return ExifInterface.ORIENTATION_NORMAL\n  }\n}\n\nfun Bitmap.toArgb8888(): Bitmap {\n  if (config == Bitmap.Config.ARGB_8888) return this\n  val converted = copy(Bitmap.Config.ARGB_8888, false)\n  recycle()\n  return converted ?: throw IOException(\"could not convert bitmap to ARGB_8888\")\n}\n\n// Force ARGB_8888 first: the native rotate needs a lockable 8888 buffer and allocates w*h*4.\nfun rotateToNativeBuffer(bitmap: Bitmap, orientation: Int): Map<String, Long> {\n  val src = bitmap.toArgb8888()\n  try {\n    val info = IntArray(3)\n    val pointer = NativeImage.rotate(src, orientation, info)\n    if (pointer == 0L) throw IOException(\"native rotate failed for orientation $orientation\")\n    return mapOf(\n      \"pointer\" to pointer,\n      \"width\" to info[0].toLong(),\n      \"height\" to info[1].toLong(),\n      \"rowBytes\" to info[2].toLong()\n    )\n  } finally {\n    if (!src.isRecycled) src.recycle()\n  }","sourceCodeStart":31,"sourceCodeEnd":67,"githubUrl":"https://github.com/immich-app/immich/blob/e55ac299a4ec7cb372e35dbf2c6c05ee9ce77f6c/mobile/android/app/src/main/kotlin/app/alextran/immich/images/RawOrientation.kt#L31-L67","documentation":"Bitmap.toArgb8888() converts a bitmap to ARGB_8888 (required by the native rotate code, which locks an 8888 buffer). It recycles the source bitmap and returns the converted copy; if Bitmap.copy returns null (allocation failure), it throws IOException 'could not convert bitmap to ARGB_8888'.","triggerScenarios":"Bitmap.copy(CONFIG_ARGB_8888, false) returning null, which happens on allocation failure for very large images or under heavy memory pressure in the process.","commonSituations":"Processing huge RAW/megapixel photos on low-memory devices; many concurrent thumbnail jobs exhausting the Java heap; greedy allocation after other bitmap work hasn't been recycled.","solutions":["Retry the operation after freeing memory — recycle other bitmaps and reduce concurrent image jobs.","Downscale the image (sample size / inSampleSize) before conversion so the 8888 copy fits in memory.","Catch the IOException and mark the asset as failed instead of crashing the thumbnail pipeline."],"exampleFix":"// before\nval converted = copy(Bitmap.Config.ARGB_8888, false)\n// after (caller-side guard)\nopts.inSampleSize = computeSampleSize(bounds, targetPx)\nval bmp = decodeBoundsAware(opts) // smaller bitmap before toArgb8888()","handlingStrategy":"try-catch","validationCode":"// Kotlin: check dimensions/heap before conversion\nif (bitmap.width.toLong() * bitmap.height > 40_000_000L) downsampleFirst()","typeGuard":null,"tryCatchPattern":"try {\n  val argb = bitmap.toArgb8888()\n} catch (e: IOException) {\n  logger.warn(\"conversion failed, retrying downscaled\", e)\n  processDownscaled(bitmap)\n}","preventionTips":["Cap concurrent bitmap jobs on low-memory devices.","Use inSampleSize to decode at target resolution.","Recycle intermediates promptly (as the code already does)."],"tags":["android","bitmap","memory","image"],"backgroundTag":"file-read-failed","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"}