{"record":{"id":"380ca354e396fbee","repo":"beemdevelopment/Aegis","slug":"unable-to-decode-stream-to-bitmap","errorCode":null,"errorMessage":"Unable to decode stream to bitmap","messagePattern":"Unable to decode stream to bitmap","errorType":"exception","errorClass":"DecodeError","httpStatus":null,"severity":"error","filePath":"app/src/main/java/com/beemdevelopment/aegis/helpers/QrCodeHelper.java","lineNumber":46,"sourceCode":"    private QrCodeHelper() {\n\n    }\n\n    public static Result decodeFromSource(LuminanceSource source) throws NotFoundException {\n        Map<DecodeHintType, Object> hints = new HashMap<>();\n        hints.put(DecodeHintType.POSSIBLE_FORMATS, Collections.singletonList(BarcodeFormat.QR_CODE));\n        hints.put(DecodeHintType.ALSO_INVERTED, true);\n\n        BinaryBitmap bitmap = new BinaryBitmap(new HybridBinarizer(source));\n        MultiFormatReader reader = new MultiFormatReader();\n        return reader.decode(bitmap, hints);\n    }\n\n    public static Result decodeFromStream(InputStream inStream) throws DecodeError {\n        BitmapFactory.Options bmOptions = new BitmapFactory.Options();\n        Bitmap bitmap = BitmapFactory.decodeStream(inStream, null, bmOptions);\n        if (bitmap == null) {\n            throw new DecodeError(\"Unable to decode stream to bitmap\");\n        }\n\n        // If ZXing is not able to decode the image on the first try, we try a couple of\n        // more times with smaller versions of the same image.\n        for (int i = 0; i <= 2; i++) {\n            if (i != 0) {\n                bitmap = BitmapHelper.resize(bitmap, bitmap.getWidth() / (i * 2), bitmap.getHeight() / (i * 2));\n            }\n\n            try {\n                int[] pixels = new int[bitmap.getWidth() * bitmap.getHeight()];\n                bitmap.getPixels(pixels, 0, bitmap.getWidth(), 0, 0, bitmap.getWidth(), bitmap.getHeight());\n\n                LuminanceSource source = new RGBLuminanceSource(bitmap.getWidth(), bitmap.getHeight(), pixels);\n                return decodeFromSource(source);\n            } catch (NotFoundException ignored) {\n\n            }","sourceCodeStart":28,"sourceCodeEnd":64,"githubUrl":"https://github.com/beemdevelopment/Aegis/blob/d6f4e5925a97e4e91593f1542085eae03432a759/app/src/main/java/com/beemdevelopment/aegis/helpers/QrCodeHelper.java#L28-L64","documentation":"QrCodeHelper.decodeFromStream wraps BitmapFactory.decodeStream, which returns null when the InputStream does not contain a decodable bitmap (corrupt data, unsupported format, truncated stream). Aegis throws DecodeError immediately instead of passing the null Bitmap to ZXing. This is the app's way of signaling that the selected image is not a usable picture before QR decoding is even attempted.","triggerScenarios":"Calling QrCodeHelper.decodeFromStream with an InputStream whose bytes BitmapFactory cannot decode: a non-image file (e.g. a PDF or text file picked by the user), a truncated or partially downloaded image, an unsupported codec, or an already-closed stream.","commonSituations":"User picks a corrupt image from the gallery or a cloud-synced placeholder that hasn't downloaded; a file manager hands over a stream that Android's BitmapFactory can't handle (e.g. exotic HEIC/WebP variants on old devices); importing a QR screenshot that was saved as a zero-byte or damaged file.","solutions":["Verify the picked file is a real, fully-downloaded image (open it in the gallery) before importing","Re-export or re-save the QR code image, preferring PNG or JPEG","Check available memory; very large images can fail to decode — resize before decoding","If reading from a content URI, take a persistable, non-expired stream from the provider"],"exampleFix":"// before\nInputStream in = context.getContentResolver().openInputStream(uri);\nResult r = QrCodeHelper.decodeFromStream(in);\n// after\ntry (InputStream in = context.getContentResolver().openInputStream(uri)) {\n    byte[] data = IOUtils.readAll(in);\n    BitmapFactory.Options opts = new BitmapFactory.Options();\n    opts.inJustDecodeBounds = true;\n    BitmapFactory.decodeByteArray(data, 0, data.length, opts);\n    if (opts.outWidth <= 0) {\n        throw new DecodeError(\"Not a valid image\");\n    }\n    Result r = QrCodeHelper.decodeFromStream(new ByteArrayInputStream(data));\n}","handlingStrategy":"validation","validationCode":"BitmapFactory.Options opts = new BitmapFactory.Options();\nopts.inJustDecodeBounds = true;\nBitmapFactory.decodeStream(stream, null, opts);\nif (opts.outWidth <= 0 || opts.outHeight <= 0) throw new DecodeError(\"Not a decodable image\");","typeGuard":null,"tryCatchPattern":"try {\n    Result r = QrCodeHelper.decodeFromStream(in);\n} catch (DecodeError e) {\n    Toast.makeText(context, R.string.error_not_an_image, Toast.LENGTH_LONG).show();\n}","preventionTips":["Pre-decode with inJustDecodeBounds to validate before real decode","Ask users to re-export screenshots as PNG/JPEG","Read the stream into memory once; avoid double-consuming closed streams","Handle content-URIs that may return placeholder or zero-byte data"],"tags":["android","bitmap","qr-code","image-decoding"],"backgroundTag":"image-decode-failed","analyzedSha":"d6f4e5925a97e4e91593f1542085eae03432a759","analyzedAt":"2026-09-08T00:46:31.111Z","contentChangedAt":"2026-09-08T00:46:31.111Z","schemaVersion":2},"datasetVersion":"2026-09-16T09:17:16.951Z"}