Skip to content

Commit 61386c5

Browse files
committed
Fix profile selection errors and EDT threading violations
Fixes #6039 This commit addresses two related issues: 1. IllegalStateException: "You don't have access to the resource" - Modified listRegionProfiles() to return null for empty profiles instead of throwing an IllegalStateException - Updated callers to handle null/empty profiles gracefully 2. EDT Threading Violation when selecting Q profile - Wrapped QRegionProfileDialog.show() in runInEdt to ensure dialog is shown on Event Dispatch Thread - Made AsyncComboBox.repaint() calls EDT-safe using invokeLater Files changed: - QRegionProfileManager.kt: Return null for empty profiles - ChatCommunicationManager.kt: Add runInEdt wrapper for dialog - CodeWhispererModelConfigurator.kt: Handle null profiles gracefully - QRegionProfileDialog.kt: Handle empty profiles with telemetry - AsyncComboBox.kt: EDT-safe repaint calls
1 parent feadd1b commit 61386c5

6 files changed

Lines changed: 41 additions & 14 deletions

File tree

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
{
2+
"type" : "bugfix",
3+
"description" : "Amazon Q: Fix profile selection errors and EDT threading violations (#6039)"
4+
}

plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/flareChat/ChatCommunicationManager.kt

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ package software.aws.toolkits.jetbrains.services.amazonq.lsp.flareChat
55

66
import com.google.gson.Gson
77
import com.google.gson.JsonObject
8+
import com.intellij.openapi.application.runInEdt
89
import com.intellij.openapi.components.Service
910
import com.intellij.openapi.components.service
1011
import com.intellij.openapi.project.Project
@@ -203,10 +204,13 @@ class ChatCommunicationManager(private val project: Project, private val cs: Cor
203204
project.messageBus.syncPublisher(QRegionProfileSelectedListener.TOPIC)
204205
.onProfileSelected(project, QRegionProfileManager.getInstance().activeProfile(project))
205206
} else {
206-
QRegionProfileDialog(
207-
project,
208-
selectedProfile = null
209-
).show()
207+
// Wrap in runInEdt to ensure dialog is shown on Event Dispatch Thread
208+
runInEdt {
209+
QRegionProfileDialog(
210+
project,
211+
selectedProfile = null
212+
).show()
213+
}
210214
}
211215

212216
return

plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/profile/QRegionProfileDialog.kt

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -75,10 +75,21 @@ class QRegionProfileDialog(
7575

7676
combo.proposeModelUpdate { model ->
7777
try {
78-
QRegionProfileManager.getInstance().listRegionProfiles(project)?.forEach {
79-
model.addElement(it)
80-
} ?: error("Attempted to fetch profiles while there does not exist")
81-
78+
val profiles = QRegionProfileManager.getInstance().listRegionProfiles(project)
79+
// Handle empty/null profiles gracefully instead of throwing
80+
if (profiles.isNullOrEmpty()) {
81+
val conn = ToolkitConnectionManager.getInstance(project).activeConnectionForFeature(QConnection.getInstance()) as? AwsBearerTokenConnection
82+
Telemetry.amazonq.didSelectProfile.use { span ->
83+
span.source(QProfileSwitchIntent.User.value)
84+
.amazonQProfileRegion("not-set")
85+
.ssoRegion(conn?.region)
86+
.credentialStartUrl(conn?.startUrl)
87+
.result(MetricResult.Failed)
88+
.reason("No profiles available")
89+
}
90+
return@proposeModelUpdate
91+
}
92+
profiles.forEach { model.addElement(it) }
8293
model.selectedItem = selectedProfile
8394
} catch (e: Exception) {
8495
val conn = ToolkitConnectionManager.getInstance(project).activeConnectionForFeature(QConnection.getInstance()) as? AwsBearerTokenConnection

plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/profile/QRegionProfileManager.kt

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -110,9 +110,11 @@ class QRegionProfileManager : PersistentStateComponent<QProfileState>, Disposabl
110110
if (mappedProfiles.size == 1) {
111111
switchProfile(project, mappedProfiles.first(), intent = QProfileSwitchIntent.Update)
112112
}
113-
mappedProfiles.takeIf { it.isNotEmpty() }?.also {
114-
connectionIdToProfileCount[connection.id] = it.size
115-
} ?: error("You don't have access to the resource")
113+
// Return null for empty profiles instead of throwing, let callers handle gracefully
114+
if (mappedProfiles.isNotEmpty()) {
115+
connectionIdToProfileCount[connection.id] = mappedProfiles.size
116+
}
117+
mappedProfiles.takeIf { it.isNotEmpty() }
116118
} catch (e: Exception) {
117119
if (e is AccessDeniedException) {
118120
LOG.warn { "Failed to list region profiles: ${e.message}" }

plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/codewhisperer/customization/CodeWhispererModelConfigurator.kt

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -130,7 +130,10 @@ class DefaultCodeWhispererModelConfigurator(
130130
calculateIfIamIdentityCenterConnection(project) {
131131
// 1. fetch all profiles, invoke fetch customizations API and get result for each profile and aggregate all the results
132132
val profiles = QRegionProfileManager.getInstance().listRegionProfiles(project)
133-
?: error("Attempted to fetch profiles while there does not exist")
133+
// Return empty list when no profiles available instead of throwing
134+
if (profiles.isNullOrEmpty()) {
135+
return@calculateIfIamIdentityCenterConnection emptyList()
136+
}
134137

135138
val customizations = profiles.flatMap { profile ->
136139
runCatching {

plugins/core/jetbrains-community/src/software/aws/toolkits/jetbrains/ui/AsyncComboBox.kt

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
package software.aws.toolkits.jetbrains.ui
55

66
import com.intellij.openapi.Disposable
7+
import com.intellij.openapi.application.ApplicationManager
78
import com.intellij.openapi.application.ModalityState
89
import com.intellij.openapi.progress.EmptyProgressIndicator
910
import com.intellij.openapi.progress.ProgressIndicator
@@ -92,7 +93,8 @@ class AsyncComboBox<T> private constructor(
9293
currentIndicator?.cancel()
9394
loading.set(true)
9495
removeAllItems()
95-
repaint()
96+
// Ensure repaint happens on EDT for thread safety
97+
ApplicationManager.getApplication().invokeLater { repaint() }
9698
val indicator = EmptyProgressIndicator(ModalityState.any()).also {
9799
currentIndicator = it
98100
}
@@ -105,7 +107,8 @@ class AsyncComboBox<T> private constructor(
105107
newModel.invoke(delegatedComboBoxModel(indicator))
106108
}.invokeOnCompletion {
107109
loading.set(false)
108-
repaint()
110+
// Ensure repaint happens on EDT for thread safety
111+
ApplicationManager.getApplication().invokeLater { repaint() }
109112
}
110113
},
111114
indicator

0 commit comments

Comments
 (0)