Tencent/QMUI_Android · error · IOException

Failed to get output stream.

Error message

Failed to get output stream.

What it means

After the MediaStore record is created, saveToStore calls contentResolver.openOutputStream(uri). If the provider refuses to hand out an output stream (null), an IOException("Failed to get output stream.") is thrown so the write aborts cleanly and pending cleanup runs.

Source

Thrown at photo/src/main/java/com/qmuiteam/photo/util/QMUIPhotoHelper.kt:71

            put(MediaStore.MediaColumns.DATE_ADDED, System.currentTimeMillis())
            put(MediaStore.MediaColumns.MIME_TYPE, mimeType)
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
                put(MediaStore.MediaColumns.RELATIVE_PATH, dirName)
                put(MediaStore.MediaColumns.IS_PENDING, 1)
            }
        }

        val contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI
        var stream: OutputStream? = null
        var uri: Uri? = null
        try {
            uri = context.contentResolver.insert(contentUri, contentValues)
            if (uri == null) {
                throw IOException("Failed to create new MediaStore record.")
            }
            stream = context.contentResolver.openOutputStream(uri)
            if (stream == null) {
                throw IOException("Failed to get output stream.")
            }
            writer.invoke(stream)
            contentValues.clear()
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
                contentValues.put(MediaStore.MediaColumns.IS_PENDING, 0)
                context.contentResolver.update(uri, contentValues, null, null)
            }
            return uri
        } catch (e: Throwable) {
            Log.i(TAG, "saveToStore failed.", e)
            if (uri != null) {
                context.contentResolver.delete(uri, null, null)
            }
        } finally {
            stream?.close()
        }
        return null
    }

View on GitHub (pinned to 026e7d4866)

Solutions

  1. Retry the save operation; transient provider failures are common.
  2. Check free storage space before writing.
  3. Verify the returned Uri is still valid (contentResolver.getType(uri) != null) before opening the stream.
  4. Catch the IOException and delete the orphan pending record if needed.

Example fix

// before
stream = context.contentResolver.openOutputStream(uri) ?: throw IOException(...)
// after
try { saveToStore(...) } catch (e: IOException) { cleanupPendingUri(uri); retryLater() }
Defensive patterns

Strategy: retry

Validate before calling

val stream = context.contentResolver.openOutputStream(uri)
if (stream == null) { /* schedule retry or cleanup pending record */ }

Try / catch

var attempt = 0
while (attempt < 3) {
  try { saveToStore(...); break } catch (e: IOException) { if (++attempt == 3) report(e) }
}

Prevention

When it happens

Trigger: openOutputStream(uri) returning null even though insert succeeded — provider locked the file, IS_PENDING conflict, storage suddenly unavailable, or security exception surfaced as null.

Common situations: Concurrent modification of the MediaStore entry; low storage making the file unopenable; OEM provider quirks; passing a uri that was invalidated after insert.

Understand the failure class

Background: "open() failed", "failed to open file", "cannot create file" — what a file open error means and how to fix it — this error's family across 42 libraries.

Related errors


AI-assisted analysis of Tencent/QMUI_Android@026e7d4866 (2026-09-06). Data as JSON: /api/errors/fa4eeb814ad4689f. Report an issue: GitHub.