mirror of
https://github.com/Kilo-Org/kilocode.git
synced 2026-08-28 19:11:03 +08:00
fix(jetbrains): don't surface workspace fetch failures as IDE errors
Retry exhaustion for workspace/app data loads logged via IntelliJ's Logger.error, which IntelliJ turns into an 'IDE Internal Error' popup. Downgrade the exhausted-retry log to warn and enrich workspace fetch failures with HTTP status and response body so the real cause (e.g. a 500 from the CLI /agent endpoint) is visible in diagnostics instead of a generic crash-style dialog.
This commit is contained in:
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"@kilocode/kilo-jetbrains": patch
|
||||
---
|
||||
|
||||
Avoid showing JetBrains internal error popups when Kilo workspace data fails to load, and include HTTP status and response details in diagnostics.
|
||||
+1
-1
@@ -786,7 +786,7 @@ class KiloBackendAppService private constructor(
|
||||
delay(RETRY_DELAY_MS)
|
||||
}
|
||||
}
|
||||
log.error("$name: all $MAX_RETRIES attempts failed")
|
||||
log.warn("$name: all $MAX_RETRIES attempts failed${last.error?.let { ": ${error(it)}" } ?: ""}")
|
||||
return last
|
||||
}
|
||||
|
||||
|
||||
+46
-5
@@ -6,6 +6,10 @@ import ai.kilocode.backend.app.SseEvent
|
||||
import ai.kilocode.backend.cli.KiloCliDataParser
|
||||
import ai.kilocode.log.KiloLog
|
||||
import ai.kilocode.jetbrains.api.client.DefaultApi
|
||||
import ai.kilocode.jetbrains.api.infrastructure.ClientError
|
||||
import ai.kilocode.jetbrains.api.infrastructure.ClientException
|
||||
import ai.kilocode.jetbrains.api.infrastructure.ServerError
|
||||
import ai.kilocode.jetbrains.api.infrastructure.ServerException
|
||||
import ai.kilocode.jetbrains.api.model.Agent
|
||||
import ai.kilocode.rpc.dto.SessionDto
|
||||
import ai.kilocode.rpc.dto.SessionListDto
|
||||
@@ -231,7 +235,8 @@ class KiloBackendWorkspace(
|
||||
} catch (e: CancellationException) {
|
||||
throw e
|
||||
} catch (e: Exception) {
|
||||
log.warn("Agents fetch failed: ${e.message}", e)
|
||||
log.warn("Agents fetch failed: ${detail(e)}", e)
|
||||
logResponseBody("agents", e)
|
||||
FetchResult.fail("agents", e)
|
||||
}
|
||||
}
|
||||
@@ -260,7 +265,8 @@ class KiloBackendWorkspace(
|
||||
} catch (e: CancellationException) {
|
||||
throw e
|
||||
} catch (e: Exception) {
|
||||
log.warn("Skills fetch failed: ${e.message}", e)
|
||||
log.warn("Skills fetch failed: ${detail(e)}", e)
|
||||
logResponseBody("skills", e)
|
||||
FetchResult.fail("skills", e)
|
||||
}
|
||||
}
|
||||
@@ -301,19 +307,34 @@ class KiloBackendWorkspace(
|
||||
delay(RETRY_DELAY_MS)
|
||||
}
|
||||
}
|
||||
log.error("$name: all $MAX_RETRIES attempts failed")
|
||||
log.warn("$name: all $MAX_RETRIES attempts failed${last.error?.let { ": ${error(it)}" } ?: ""}")
|
||||
return last
|
||||
}
|
||||
|
||||
private fun setWorkspaceError(message: String, errors: List<LoadError>) {
|
||||
log.warn("Workspace error [$directory]: $message")
|
||||
val text = if (errors.isEmpty()) message else "$message [${errors.joinToString("; ") { error(it) }}]"
|
||||
log.warn("Workspace error [$directory]: $text")
|
||||
_state.value = KiloWorkspaceState.Error(message, errors)
|
||||
}
|
||||
|
||||
private fun logResponseBody(resource: String, e: Exception) {
|
||||
val body = body(e) ?: return
|
||||
log.warn("$resource response body: $body")
|
||||
}
|
||||
|
||||
private fun error(err: LoadError): String {
|
||||
val status = err.status?.let { " status=$it" } ?: ""
|
||||
val detail = err.detail?.let { " detail=$it" } ?: ""
|
||||
return "${err.resource}$status$detail"
|
||||
}
|
||||
|
||||
private data class FetchResult<T>(val value: T?, val error: LoadError?) {
|
||||
companion object {
|
||||
fun <T> ok(value: T) = FetchResult<T>(value, null)
|
||||
fun <T> fail(resource: String, e: Exception? = null) = FetchResult<T>(null, LoadError(resource, detail = e?.message))
|
||||
fun <T> fail(resource: String, e: Exception? = null) = FetchResult<T>(
|
||||
null,
|
||||
LoadError(resource, status = e?.let(::status), detail = e?.let(::detail)),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -322,3 +343,23 @@ class KiloBackendWorkspace(
|
||||
}
|
||||
|
||||
private fun encode(value: String) = java.net.URLEncoder.encode(value, Charsets.UTF_8)
|
||||
|
||||
private fun status(e: Exception): Int? = when (e) {
|
||||
is ClientException -> e.statusCode
|
||||
is ServerException -> e.statusCode
|
||||
else -> null
|
||||
}
|
||||
|
||||
private fun body(e: Exception): String? = when (e) {
|
||||
is ClientException -> (e.response as? ClientError<*>)?.body?.toString()
|
||||
is ServerException -> (e.response as? ServerError<*>)?.body?.toString()
|
||||
else -> null
|
||||
}
|
||||
|
||||
private fun detail(e: Exception): String? {
|
||||
val body = body(e)
|
||||
val status = status(e)
|
||||
if (status != null && !body.isNullOrBlank()) return "HTTP $status: $body"
|
||||
if (status != null) return "HTTP $status: ${e.message}"
|
||||
return e.message
|
||||
}
|
||||
|
||||
+2
@@ -522,6 +522,8 @@ class KiloBackendAppServiceTest {
|
||||
assertTrue(log.messages.any {
|
||||
it.contains("App error: Failed to load required data") && it.contains("config")
|
||||
})
|
||||
assertTrue(log.messages.any { it.contains("WARN: config: all 3 attempts failed") })
|
||||
assertTrue(log.messages.none { it.contains("ERROR: config: all 3 attempts failed") })
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
+7
@@ -232,6 +232,7 @@ class KiloBackendWorkspaceTest {
|
||||
@Test
|
||||
fun `agents failure retries then transitions to Error`() = runBlocking {
|
||||
mock.agentsStatus = 500
|
||||
mock.agents = """{"error":"invalid agent config"}"""
|
||||
val app = setup()
|
||||
val ws = ready(app)
|
||||
|
||||
@@ -241,6 +242,12 @@ class KiloBackendWorkspaceTest {
|
||||
|
||||
val err = ws.state.value as KiloWorkspaceState.Error
|
||||
assertTrue(err.message.contains("agents"))
|
||||
val item = err.errors.single { it.resource == "agents" }
|
||||
assertEquals(500, item.status)
|
||||
assertTrue(item.detail?.contains("invalid agent config") == true)
|
||||
assertTrue(log.messages.any { it.contains("agents response body:") && it.contains("invalid agent config") })
|
||||
assertTrue(log.messages.any { it.contains("WARN: agents: all 3 attempts failed") })
|
||||
assertTrue(log.messages.none { it.contains("ERROR: agents: all 3 attempts failed") })
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
Reference in New Issue
Block a user