Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

fix: NPE during error reporting#186

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to ourterms of service andprivacy statement. We’ll occasionally send you account related emails.

Already on GitHub?Sign in to your account

Merged
fioan89 merged 2 commits intomainfromfix-npe-during-error-reporting
Sep 2, 2025
Merged
Show file tree
Hide file tree
Changes fromall commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletionsCHANGELOG.md
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -6,6 +6,10 @@

- improved diagnose support

### Fixed

- NPE during error reporting

## 0.6.3 - 2025-08-25

### Added
Expand Down
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -409,7 +409,6 @@ class CoderRemoteProvider(
context.logger.info("Displaying ${client.url} in the UI")
pollJob = poll(client, cli)
context.logger.info("Workspace poll job created with reference $pollJob")
context.envPageManager.showPluginEnvironmentsPage()
}

private fun MutableStateFlow<LoadableState<List<CoderRemoteEnvironment>>>.showLoadingMessage() {
Expand Down
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -3,22 +3,19 @@ package com.coder.toolbox.views
import com.coder.toolbox.CoderToolboxContext
import com.coder.toolbox.cli.CoderCLIManager
import com.coder.toolbox.sdk.CoderRestClient
import com.coder.toolbox.sdk.ex.APIResponseException
import com.coder.toolbox.views.state.CoderCliSetupWizardState
import com.coder.toolbox.views.state.WizardStep
import com.jetbrains.toolbox.api.remoteDev.ProviderVisibilityState
import com.jetbrains.toolbox.api.ui.actions.RunnableActionDescription
import com.jetbrains.toolbox.api.ui.components.UiField
import kotlinx.coroutines.CoroutineName
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch
import java.util.UUID

class CoderCliSetupWizardPage(
private val context: CoderToolboxContext,
private val settingsPage: CoderSettingsPage,
private valvisibilityState:MutableStateFlow<ProviderVisibilityState>,
visibilityState:StateFlow<ProviderVisibilityState>,
initialAutoSetup: Boolean = false,
jumpToMainPageOnError: Boolean = false,
onConnect: suspend (
Expand All@@ -31,33 +28,28 @@ class CoderCliSetupWizardPage(
context.ui.showUiPage(settingsPage)
})

private val deploymentUrlStep = DeploymentUrlStep(context,this::notify)
private val deploymentUrlStep = DeploymentUrlStep(context,visibilityState)
private val tokenStep = TokenStep(context)
private val connectStep = ConnectStep(
context,
shouldAutoLogin = shouldAutoSetup,
jumpToMainPageOnError,
this::notify,
visibilityState,
this::displaySteps,
onConnect
)
private val errorReporter = ErrorReporter.create(context, visibilityState, this.javaClass)

/**
* Fields for this page, displayed in order.
*/
override val fields: MutableStateFlow<List<UiField>> = MutableStateFlow(emptyList())
override val actionButtons: MutableStateFlow<List<RunnableActionDescription>> = MutableStateFlow(emptyList())

private val errorBuffer = mutableListOf<Throwable>()

override fun beforeShow() {
displaySteps()
if (errorBuffer.isNotEmpty() && visibilityState.value.applicationVisible) {
errorBuffer.forEach {
showError(it)
}
errorBuffer.clear()
}
errorReporter.flush()
}

private fun displaySteps() {
Expand DownExpand Up@@ -124,30 +116,5 @@ class CoderCliSetupWizardPage(
/**
* Show an error as a popup on this page.
*/
fun notify(logPrefix: String, ex: Throwable) {
context.logger.error(ex, logPrefix)
if (!visibilityState.value.applicationVisible) {
context.logger.debug("Toolbox is not yet visible, scheduling error to be displayed later")
errorBuffer.add(ex)
return
}
showError(ex)
}

private fun showError(ex: Throwable) {
val textError = if (ex is APIResponseException) {
if (!ex.reason.isNullOrBlank()) {
ex.reason
} else ex.message
} else ex.message

context.cs.launch(CoroutineName("Coder Setup Visual Error Reporting")) {
context.ui.showSnackbar(
UUID.randomUUID().toString(),
context.i18n.ptrl("Error encountered while setting up Coder"),
context.i18n.pnotr(textError ?: ""),
context.i18n.ptrl("Dismiss")
)
}
}
fun notify(message: String, ex: Throwable) = errorReporter.report(message, ex)
}
25 changes: 15 additions & 10 deletionssrc/main/kotlin/com/coder/toolbox/views/ConnectStep.kt
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -7,6 +7,7 @@ import com.coder.toolbox.plugin.PluginManager
import com.coder.toolbox.sdk.CoderRestClient
import com.coder.toolbox.views.state.CoderCliSetupContext
import com.coder.toolbox.views.state.CoderCliSetupWizardState
import com.jetbrains.toolbox.api.remoteDev.ProviderVisibilityState
import com.jetbrains.toolbox.api.ui.components.LabelField
import com.jetbrains.toolbox.api.ui.components.RowGroup
import com.jetbrains.toolbox.api.ui.components.ValidationErrorField
Expand All@@ -27,24 +28,23 @@ class ConnectStep(
private val context: CoderToolboxContext,
private val shouldAutoLogin: StateFlow<Boolean>,
private val jumpToMainPageOnError: Boolean,
private val notify: (String, Throwable) -> Unit,
visibilityState: StateFlow<ProviderVisibilityState>,
private val refreshWizard: () -> Unit,
private val onConnect: suspend (
client: CoderRestClient,
cli: CoderCLIManager,
) -> Unit,
private val onConnect: suspend (client: CoderRestClient, cli: CoderCLIManager) -> Unit,
) : WizardStep {
private var signInJob: Job? = null

private val statusField = LabelField(context.i18n.pnotr(""))
private val errorField = ValidationErrorField(context.i18n.pnotr(""))
private val errorReporter = ErrorReporter.create(context, visibilityState, this.javaClass)

override val panel: RowGroup = RowGroup(
RowGroup.RowField(statusField),
RowGroup.RowField(errorField)
)

override fun onVisible() {
errorReporter.flush()
errorField.textState.update {
context.i18n.pnotr("")
}
Expand DownExpand Up@@ -73,6 +73,9 @@ class ConnectStep(
errorField.textState.update { context.i18n.ptrl("Token is required") }
return
}
// Capture the host name early for error reporting
val hostName = CoderCliSetupContext.url!!.host

signInJob?.cancel()
signInJob = context.cs.launch(CoroutineName("Http and CLI Setup")) {
try {
Expand DownExpand Up@@ -100,21 +103,23 @@ class ConnectStep(
yield()
cli.login(client.token!!)
}
logAndReportProgress("Successfully configured ${CoderCliSetupContext.url!!.host}...")
logAndReportProgress("Successfully configured ${hostName}...")
// allows interleaving with the back/cancel action
yield()
CoderCliSetupContext.reset()
CoderCliSetupWizardState.goToFirstStep()
context.logger.info("Connection setup done, initializing the workspace poller...")
onConnect(client, cli)

CoderCliSetupContext.reset()
CoderCliSetupWizardState.goToFirstStep()
context.envPageManager.showPluginEnvironmentsPage()
} catch (ex: CancellationException) {
if (ex.message != USER_HIT_THE_BACK_BUTTON) {
notify("Connection to ${CoderCliSetupContext.url!!.host} was configured", ex)
errorReporter.report("Connection to $hostName was configured", ex)
handleNavigation()
refreshWizard()
}
} catch (ex: Exception) {
notify("Failed to configure ${CoderCliSetupContext.url!!.host}", ex)
errorReporter.report("Failed to configure $hostName", ex)
handleNavigation()
refreshWizard()
}
Expand Down
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -6,13 +6,15 @@ import com.coder.toolbox.util.toURL
import com.coder.toolbox.util.validateStrictWebUrl
import com.coder.toolbox.views.state.CoderCliSetupContext
import com.coder.toolbox.views.state.CoderCliSetupWizardState
import com.jetbrains.toolbox.api.remoteDev.ProviderVisibilityState
import com.jetbrains.toolbox.api.ui.components.CheckboxField
import com.jetbrains.toolbox.api.ui.components.LabelField
import com.jetbrains.toolbox.api.ui.components.LabelStyleType
import com.jetbrains.toolbox.api.ui.components.RowGroup
import com.jetbrains.toolbox.api.ui.components.TextField
import com.jetbrains.toolbox.api.ui.components.TextType
import com.jetbrains.toolbox.api.ui.components.ValidationErrorField
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.update
import java.net.MalformedURLException
import java.net.URL
Expand All@@ -25,9 +27,11 @@ import java.net.URL
*/
class DeploymentUrlStep(
private val context: CoderToolboxContext,
private val notify: (String, Throwable) -> Unit
visibilityState: StateFlow<ProviderVisibilityState>,
) :
WizardStep {
private val errorReporter = ErrorReporter.create(context, visibilityState, this.javaClass)

private val urlField = TextField(context.i18n.ptrl("Deployment URL"), "", TextType.General)
private val emptyLine = LabelField(context.i18n.pnotr(""), LabelStyleType.Normal)

Expand DownExpand Up@@ -66,6 +70,7 @@ class DeploymentUrlStep(
signatureFallbackStrategyField.checkedState.update {
context.settingsStore.fallbackOnCoderForSignatures.isAllowed()
}
errorReporter.flush()
}

override fun onNext(): Boolean {
Expand All@@ -78,7 +83,7 @@ class DeploymentUrlStep(
try {
CoderCliSetupContext.url = validateRawUrl(url)
} catch (e: MalformedURLException) {
notify("URL is invalid", e)
errorReporter.report("URL is invalid", e)
return false
}
if (context.settingsStore.requireTokenAuth) {
Expand Down
73 changes: 73 additions & 0 deletionssrc/main/kotlin/com/coder/toolbox/views/ErrorReporter.kt
View file
Open in desktop
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
package com.coder.toolbox.views

import com.coder.toolbox.CoderToolboxContext
import com.coder.toolbox.sdk.ex.APIResponseException
import com.jetbrains.toolbox.api.remoteDev.ProviderVisibilityState
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.launch
import java.util.UUID

sealed class ErrorReporter {

/**
* Logs and show errors as popups.
*/
abstract fun report(message: String, ex: Throwable)

/**
* Processes any buffered errors when the application becomes visible.
*/
abstract fun flush()

companion object {
fun create(
context: CoderToolboxContext,
visibilityState: StateFlow<ProviderVisibilityState>,
callerClass: Class<*>
): ErrorReporter = ErrorReporterImpl(context, visibilityState, callerClass)
}
}

private class ErrorReporterImpl(
private val context: CoderToolboxContext,
private val visibilityState: StateFlow<ProviderVisibilityState>,
private val callerClass: Class<*>
) : ErrorReporter() {
private val errorBuffer = mutableListOf<Throwable>()

override fun report(message: String, ex: Throwable) {
context.logger.error(ex, "[${callerClass.simpleName}] $message")
if (!visibilityState.value.applicationVisible) {
context.logger.debug("Toolbox is not yet visible, scheduling error to be displayed later")
errorBuffer.add(ex)
return
}
showError(ex)
}

private fun showError(ex: Throwable) {
val textError = if (ex is APIResponseException) {
if (!ex.reason.isNullOrBlank()) {
ex.reason
} else ex.message
} else ex.message ?: ex.toString()
context.cs.launch {
context.ui.showSnackbar(
UUID.randomUUID().toString(),
context.i18n.ptrl("Error encountered while setting up Coder"),
context.i18n.pnotr(textError ?: ""),
context.i18n.ptrl("Dismiss")
)
}
}


override fun flush() {
if (errorBuffer.isNotEmpty() && visibilityState.value.applicationVisible) {
errorBuffer.forEach {
showError(it)
}
errorBuffer.clear()
}
}
}
Loading

[8]ページ先頭

©2009-2025 Movatter.jp