{"record":{"id":"155e813a48c8dd88","repo":"microg/GmsCore","slug":"failed-to-create-directories-parentdir-absolute","errorCode":null,"errorMessage":"Failed to create directories: ${parentDir.absolutePath}","messagePattern":"Failed to create directories: (.+?)","errorType":"exception","errorClass":"IOException","httpStatus":null,"severity":"error","filePath":"vending-app/src/main/java/org/microg/vending/billing/core/HttpClient.kt","lineNumber":58,"sourceCode":"    private val client = singleInstanceOf { HttpClient(OkHttp) {\n        expectSuccess = true\n        install(HttpTimeout)\n    } }\n\n    private val clientWithCache = singleInstanceOf { HttpClient(OkHttp) {\n        expectSuccess = true\n        install(HttpCache)\n        install(HttpTimeout)\n    } }\n\n    suspend fun download(\n        url: String,\n        downloadFile: File,\n        params: Map<String, String> = emptyMap()\n    ): File = downloadFile.also { toFile ->\n        val parentDir = downloadFile.getParentFile()\n        if (parentDir != null && !parentDir.exists() && !parentDir.mkdirs()) {\n            throw IOException(\"Failed to create directories: ${parentDir.absolutePath}\")\n        }\n\n        FileOutputStream(toFile).use { download(url, it, params) }\n    }\n\n    suspend fun download(\n            url: String,\n            downloadTo: OutputStream,\n            params: Map<String, String> = emptyMap(),\n            downloadedBytes: Long = 0,\n            emitProgress: (bytesDownloaded: Long) -> Unit = {}\n    ) {\n        try {\n            Log.d(TAG, \"download downloadedBytes:$downloadedBytes\")\n            client.prepareGet(url.asUrl(params)){\n                if (downloadedBytes > 0) {\n                    headers {\n                        append(HttpHeaders.Range, \"bytes=$downloadedBytes-\")","sourceCodeStart":40,"sourceCodeEnd":76,"githubUrl":"https://github.com/microg/GmsCore/blob/157c9d86ac46c195a86c2f15ab55c84036223f95/vending-app/src/main/java/org/microg/vending/billing/core/HttpClient.kt#L40-L76","documentation":"HttpClient.download throws this IOException when the target file's parent directory does not exist and mkdirs() fails to create it. The library refuses to continue the download because writing the file would fail anyway, and includes the offending directory path in the message.","triggerScenarios":"Calling download(url, File(\"/some/path/file.bin\")) where /some/path doesn't exist and cannot be created — e.g. missing WRITE_EXTERNAL/storage permission, read-only filesystem, path on unmounted storage, or invalid path characters.","commonSituations":"Downloading to external storage without runtime storage permission on Android 6+; using a path on scoped-storage-restricted locations (Android 10+); SD card removed or emulated storage full.","solutions":["Request MANAGE_EXTERNAL_STORAGE / WRITE_EXTERNAL_STORAGE or use app-scoped storage (getExternalFilesDir) instead","Create the parent directory yourself beforehand with parentDir.mkdirs() and check the result","Verify the path exists, is writable, and the filesystem is not read-only or full","Download to internal cache dir first and copy afterwards if the destination is restricted"],"exampleFix":"// before\nhttpClient.download(url, File(\"/sdcard/downloads/file.bin\"))\n// after\nval dest = File(context.getExternalFilesDir(null), \"file.bin\")\ndest.parentFile?.mkdirs()\nhttpClient.download(url, dest)","handlingStrategy":"validation","validationCode":"val parent = destFile.parentFile\nif (parent != null && !parent.exists() && !parent.mkdirs()) {\n    throw IOException(\"Cannot create ${parent}\")\n}","typeGuard":"fun File.isWritableTarget(): Boolean {\n    val p = parentFile ?: return false\n    return (p.exists() || p.mkdirs()) && p.canWrite()\n}","tryCatchPattern":"try {\n    httpClient.download(url, destFile)\n} catch (e: IOException) {\n    if (e.message?.startsWith(\"Failed to create directories\") == true) {\n        // fall back to app-scoped storage\n    }\n}","preventionTips":["Use context.getExternalFilesDir(null) for download targets on Android","Request storage permissions at runtime before downloading","Check canWrite() on the parent directory before starting the download"],"tags":["io","filesystem","android","permissions"],"backgroundTag":"mkdir-permission-denied","analyzedSha":"157c9d86ac46c195a86c2f15ab55c84036223f95","analyzedAt":"2026-09-06T17:27:33.892Z","contentChangedAt":"2026-09-06T17:27:33.892Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}