From 2592bba2d7c47522f3d6c477431a03b496295fda Mon Sep 17 00:00:00 2001 From: Desert_Gamer Date: Wed, 15 Apr 2026 01:08:39 +0300 Subject: [PATCH] Fix Write-unsafe context error in refreshSync during New Project dialog MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit RefreshSessionImpl asserts a write-safe context in its constructor via TransactionGuard. When refreshSync was called from a coroutine dispatched inside a modal dialog (New Project wizard), TransactionGuard rejected the call with "Write-unsafe context!" because the modality state of the dialog did not satisfy the write-safe context requirements. The previous implementation wrapped RefreshQueue.refresh in a writeAction, which did not help — the assertion fires at RefreshSession creation time, before any write lock is acquired. Fix: schedule the RefreshQueue.refresh call via ApplicationManager.invokeLater with the correct ModalityState. This ensures TransactionGuard sees a write-safe context when RefreshSessionImpl is constructed. A suspendCancellableCoroutine is used to await completion without blocking EDT. --- src/main/kotlin/util/files.kt | 27 ++++++++++++++++----------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/src/main/kotlin/util/files.kt b/src/main/kotlin/util/files.kt index 3743fe684..cf356e91c 100644 --- a/src/main/kotlin/util/files.kt +++ b/src/main/kotlin/util/files.kt @@ -3,7 +3,7 @@ * * https://mcdev.io/ * - * Copyright (C) 2025 minecraft-dev + * Copyright (C) 2026 minecraft-dev * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published @@ -33,6 +33,8 @@ import java.nio.file.Path import java.util.jar.Attributes import java.util.jar.JarFile import java.util.jar.Manifest +import kotlin.coroutines.resume +import kotlinx.coroutines.suspendCancellableCoroutine val VirtualFile.localFile: File get() = VfsUtilCore.virtualToIoFile(this) @@ -80,17 +82,20 @@ operator fun Manifest.get(attribute: String): String? = mainAttributes.getValue( operator fun Manifest.get(attribute: Attributes.Name): String? = mainAttributes.getValue(attribute) suspend fun VirtualFile.refreshSync(modalityState: ModalityState): VirtualFile? { - fun refresh() { - RefreshQueue.getInstance().refresh(false, this.isDirectory, null, modalityState, this) + // RefreshSessionImpl asserts a write-safe context in its constructor, which fails when called + // from a modal dialog's dispatched coroutine. We schedule the refresh via invokeLater with + // the correct ModalityState so TransactionGuard sees a write-safe context. + suspendCancellableCoroutine { cont -> + ApplicationManager.getApplication().invokeLater( + { + RefreshQueue.getInstance().refresh(false, this.isDirectory, null, modalityState, this) + cont.resume(Unit) + }, + modalityState, + ) } - if (ApplicationManager.getApplication().isWriteAccessAllowed) { - refresh() - } else { - writeAction { - refresh() - } + return writeAction { + this@refreshSync.parent?.findOrCreateChildData(this@refreshSync, this@refreshSync.name) } - - return this.parent?.findOrCreateChildData(this, this.name) }