How to correctly call NavigatablePsiElement.navigate()

My current way is

cs.launch(Dispatchers.Default) {
    smartReadAction(project, process) {
        val element = findNavigatablePsiElement()
        invokeLater {
            element.navigate(true)
        }
    }
}

It generates an exception(only in non-production releases, such as EAP, but anyway)

java.lang.Throwable: Slow operations are prohibited on EDT. See SlowOperations.assertSlowOperationsAreAllowed javadoc.
at com.intellij.openapi.diagnostic.Logger.error(Logger.java:375)
at com.intellij.util.SlowOperations.assertSlowOperationsAreAllowed(SlowOperations.java:113)
at com.intellij.workspaceModel.core.fileIndex.impl.WorkspaceFileIndexDataImpl.ensureIsUpToDate(WorkspaceFileIndexDataImpl.kt:160)
at com.intellij.workspaceModel.core.fileIndex.impl.WorkspaceFileIndexDataImpl.getFileInfo(WorkspaceFileIndexDataImpl.kt:105)
at com.intellij.workspaceModel.core.fileIndex.impl.WorkspaceFileIndexImpl.getFileInfo(WorkspaceFileIndexImpl.kt:307)
at com.intellij.openapi.roots.impl.ProjectFileIndexImpl.isUnderIgnored(ProjectFileIndexImpl.java:86)
at com.intellij.openapi.roots.impl.ProjectFileIndexFacade.isUnderIgnored(ProjectFileIndexFacade.java:81)
at com.intellij.psi.impl.file.impl.FileManagerImpl.isExcludedOrIgnored(FileManagerImpl.java:577)
at com.intellij.psi.impl.file.impl.FileManagerImpl.findDirectoryImpl(FileManagerImpl.java:563)
at com.intellij.psi.impl.file.impl.FileManagerImpl.findDirectoryImpl(FileManagerImpl.java:567)
at com.intellij.psi.impl.file.impl.FileManagerImpl.findDirectoryImpl(FileManagerImpl.java:567)
at com.intellij.psi.impl.file.impl.FileManagerImpl.findDirectory(FileManagerImpl.java:556)
at com.intellij.psi.impl.PsiManagerImpl.findDirectory(PsiManagerImpl.java:187)
at com.intellij.psi.AbstractFileViewProvider.shouldCreatePsi(AbstractFileViewProvider.java:91)
at com.intellij.psi.MultiplePsiFilesPerDocumentFileViewProvider.getPsiInner(MultiplePsiFilesPerDocumentFileViewProvider.java:69)
at com.intellij.psi.AbstractFileViewProvider.getPsi(AbstractFileViewProvider.java:188)
at com.intellij.psi.impl.file.impl.FileManagerImpl.areViewProvidersEquivalent(FileManagerImpl.java:698)
at com.intellij.psi.impl.file.impl.FileManagerImpl.shouldResurrect(FileManagerImpl.java:803)
at com.intellij.psi.impl.file.impl.FileManagerImpl.evaluateValidity(FileManagerImpl.java:767)
at com.intellij.psi.impl.file.impl.FileManagerImpl.evaluateValidity(FileManagerImpl.java:750)
at com.intellij.psi.impl.source.PsiFileImpl.isValid(PsiFileImpl.java:173)
at com.intellij.psi.impl.source.SubstrateRef$StubRef.isValid(SubstrateRef.java:101)
at com.intellij.extapi.psi.StubBasedPsiElementBase.isValid(StubBasedPsiElementBase.java:261)
at com.intellij.ide.util.EditSourceUtil.canNavigate(EditSourceUtil.java:62)
at com.intellij.ide.util.EditSourceUtil.getDescriptor(EditSourceUtil.java:33)
at com.intellij.ide.util.PsiNavigationSupportImpl.getDescriptor(PsiNavigationSupportImpl.java:24)
at com.intellij.psi.impl.PsiElementBase.navigate(PsiElementBase.java:192)

Side note: please do not use in coroutines, switch to EDT using withContext(Dispatchers.EDT) { } outside of read actions

I think in your case the simplest option is com.intellij.util.OpenSourceUtil#navigate(com.intellij.pom.Navigatable...)

@Service(Service.Level.PROJECT)
class SearchService(private val project: Project, private val cs: CoroutineScope) {
    fun searchForString() {
        cs.launch {
            val psiClass = smartReadAction {
                JavaPsiFacade.getInstance(project).findClass("java.lang.String", allScope(project))
            }
            if (psiClass != null) {
                withContext(Dispatchers.EDT) {
                    OpenSourceUtil.navigate(psiClass)
                }
            }
        }
    }
}

Ideally, you would use NavigationService but it will become stable only in 2026.1:

val request: NavigationRequest? = smartReadAction {
    JavaPsiFacade.getInstance(project).findClass("java.lang.String", allScope(project))
        ?.navigationRequest()
}
if (request != null) {
    withContext(Dispatchers.EDT) {
        NavigationService.getInstance(project).navigate(request)
    }
}

The key difference is that NavigationService does not use any kind of modal dialogs (usually they are invisible anyway because navigation is shorter than 250ms). While OpenSourceUtil uses a modal progress and supports legacy use cases with Navigatables implementing custom navigate.

Hi, Yuriy. Thanks a lot for your answer. I already changed everything in my plugin after your last comment. And my solution looks almost the same as yours. The only difference that I use simple NavigatableElement.navigate(true). Anything wrong with this? Works fine…

Navigatable.navigate() call is not really recommended because it may access model and lead to visible delays/freezes on EDT.

Both options from my answer always access model on background threads.

E.g. plain navigate may trigger decompiler in EDT for Java .class, we had a lot of similar freezes in the past. Currently the platform mostly works through NavigationService.

Slow operations are prohibited on EDT. See SlowOperations.assertSlowOperationsAreAllowed javadoc.

Also want to highlight the fact that SlowOperations assert is not an exception, it is logged error in development/EAP to prevent new incorrect code. It is an important safety measure - it shows that here UI freeze is possible.