-
Notifications
You must be signed in to change notification settings - Fork 2.8k
Migration error handling #8010
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 our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Migration error handling #8010
Changes from 76 commits
d01d373
5742354
3b3813b
cb0f039
389db69
35efa92
722723a
01d640c
a0525c8
025ff71
515fe87
b1a1bdb
ee84ada
5003b56
d6f1469
29fbe67
04c4e7e
da7eb2e
a699aed
6bd734f
604526a
d900359
eab2e4b
5a6b0de
7be4ba1
982ca47
8e4ec58
309ed8e
70e5d51
143dd77
386596c
89d7fee
398872d
d04cba6
e5e7b01
9f10736
48634f9
f064555
23daed6
9eea8b0
9852be9
20b15eb
cb297a4
a5a73d0
c305c1e
dca9f64
87bb29b
cfc2b53
bf75212
140ed5b
631c78c
e7d3692
2617f74
e93413c
d9493c7
26b9d4e
2360ee7
b0605fe
991f0e6
7c0a5c9
f18d6ed
e9b0582
055fbf4
a199585
84f4ade
3bf1d70
9402bb3
31c1374
ab5ca21
a02cf4f
6eead10
937045b
aca60aa
43df5ba
905a7ad
0ec9d33
956fc7c
5c276bd
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,83 @@ | ||
| interface ErrorLike { | ||
| message?: unknown | ||
| status?: unknown | ||
| data?: unknown | ||
| body?: unknown | ||
| } | ||
|
|
||
| function isObject(value: unknown): value is Record<string, unknown> { | ||
| return typeof value === "object" && value !== null | ||
| } | ||
|
|
||
| function getText(value: unknown): string | undefined { | ||
| if (typeof value === "string") { | ||
| const text = value.trim() | ||
| return text || undefined | ||
| } | ||
|
|
||
| if (typeof value === "number" || typeof value === "boolean") { | ||
| return String(value) | ||
| } | ||
|
|
||
| return undefined | ||
| } | ||
|
|
||
| function getMessage(value: unknown) { | ||
| if (!isObject(value)) return undefined | ||
| return getText((value as ErrorLike).message) | ||
| } | ||
|
|
||
| function getStatus(value: unknown) { | ||
| if (!isObject(value)) return undefined | ||
| const status = (value as ErrorLike).status | ||
| return typeof status === "number" ? String(status) : getText(status) | ||
| } | ||
|
|
||
| function getBody(value: unknown) { | ||
| if (!isObject(value)) return undefined | ||
|
|
||
| const body = (value as ErrorLike).body | ||
| const text = getText(body) | ||
| if (text) return text | ||
|
|
||
| if (isObject(body)) { | ||
| const msg = getMessage(body) | ||
| if (msg) return msg | ||
| } | ||
|
|
||
| return undefined | ||
| } | ||
|
|
||
| function getData(value: unknown) { | ||
| if (!isObject(value)) return undefined | ||
|
|
||
| const data = (value as ErrorLike).data | ||
| const text = getText(data) | ||
| if (text) return text | ||
|
|
||
| if (isObject(data)) { | ||
| const msg = getMessage(data) | ||
| if (msg) return msg | ||
| } | ||
|
|
||
| return undefined | ||
| } | ||
|
|
||
| export function getMigrationErrorMessage(err: unknown) { | ||
| const message = getMessage(err) | ||
| if (message) return message | ||
|
|
||
| const body = getBody(err) | ||
| if (body) return body | ||
|
|
||
| const data = getData(err) | ||
| if (data) return data | ||
|
|
||
| const status = getStatus(err) | ||
| if (status) return `Request failed (${status})` | ||
|
|
||
| const text = getText(err) | ||
| if (text) return text | ||
|
|
||
| return "Unknown migration error" | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,6 @@ | ||
| export interface MigrationResultItem { | ||
| item: string | ||
| category: "provider" | "mcpServer" | "customMode" | "defaultModel" | "settings" | "session" | ||
| status: "success" | "warning" | "error" | ||
| message?: string | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -8,6 +8,7 @@ | |
|
|
||
| import { Show, createSignal, onMount, onCleanup } from "solid-js" | ||
| import type { Component, JSX } from "solid-js" | ||
| import { showToast } from "@kilocode/kilo-ui/toast" | ||
| import { useVSCode } from "../../context/vscode" | ||
| import { useLanguage } from "../../context/language" | ||
| import type { | ||
|
|
@@ -154,7 +155,7 @@ const WarningSvg = (): JSX.Element => ( | |
| // --------------------------------------------------------------------------- | ||
|
|
||
| type Screen = "whats-new" | "migrate" | ||
| type MigratePhase = "selecting" | "migrating" | "done" | ||
| type MigratePhase = "selecting" | "migrating" | "error" | "done" | ||
|
|
||
| interface ProgressEntry { | ||
| item: string | ||
|
|
@@ -263,7 +264,11 @@ const MigrationWizard: Component<MigrationWizardProps> = (props) => { | |
| if (msg?.type === "legacyMigrationComplete") { | ||
| const complete = msg as LegacyMigrationCompleteMessage | ||
| setResults(complete.results) | ||
| setPhase("done") | ||
| const hasErrors = complete.results.some((r) => r.status === "error") | ||
| setPhase(hasErrors ? "error" : "done") | ||
| if (!hasErrors) { | ||
| vscode.postMessage({ type: "loadSessions" }) | ||
| } | ||
| } | ||
| } | ||
|
|
||
|
|
@@ -382,6 +387,11 @@ const MigrationWizard: Component<MigrationWizardProps> = (props) => { | |
| props.onComplete() | ||
| } | ||
|
|
||
| const copySessionError = async (text: string) => { | ||
| await navigator.clipboard.writeText(text) | ||
| showToast({ variant: "success", title: language.t("migration.error.toast.copied") }) | ||
| } | ||
|
|
||
| // --------------------------------------------------------------------------- | ||
| // Data helpers | ||
| // --------------------------------------------------------------------------- | ||
|
|
@@ -431,17 +441,19 @@ const MigrationWizard: Component<MigrationWizardProps> = (props) => { | |
|
|
||
| // Group-level status for progress display | ||
| const groupStatus = (group: string): ProgressEntry["status"] => { | ||
| const entries = progressEntries().filter((e) => e.group === group) | ||
| const entries = progressEntries().filter((entry) => entry.group === group) | ||
| if (entries.length === 0) return "pending" | ||
| if (entries.some((e) => e.status === "error")) return "error" | ||
| if (entries.some((e) => e.status === "warning")) return "warning" | ||
| if (entries.every((e) => e.status === "success")) return "success" | ||
| if (entries.some((e) => e.status === "migrating")) return "migrating" | ||
| if (entries.some((entry) => entry.status === "error")) return "error" | ||
| if (entries.some((entry) => entry.status === "warning")) return "warning" | ||
| if (entries.every((entry) => entry.status === "success")) return "success" | ||
| if (entries.some((entry) => entry.status === "migrating")) return "migrating" | ||
| return "pending" | ||
| } | ||
|
|
||
| const successCount = () => results().filter((r) => r.status === "success").length | ||
| const successCount = () => results().filter((result) => result.status === "success").length | ||
| const totalCount = () => results().length | ||
| const groupMessage = (group: string) => | ||
| progressEntries().find((entry) => entry.group === group && entry.status === "error")?.message | ||
|
|
||
| // --------------------------------------------------------------------------- | ||
| // Status icon renderer | ||
|
|
@@ -677,8 +689,33 @@ const MigrationWizard: Component<MigrationWizardProps> = (props) => { | |
| </label> | ||
| </Show> | ||
| <div class="migration-wizard__item-text"> | ||
| <div class="label">Chat Sessions & History</div> | ||
| <div class="desc">{sessions().length} sessions detected</div> | ||
| <div class="label">{language.t("migration.migrate.chatHistory")}</div> | ||
| <div class="desc"> | ||
| {language.t("migration.migrate.sessionsDetected", { count: String(sessions().length) })} | ||
| </div> | ||
| <Show | ||
| when={ | ||
| (phase() === "error" || phase() === "done") && | ||
| groupStatus("sessions") === "error" && | ||
| groupMessage("sessions") | ||
| } | ||
| > | ||
| <div class="migration-wizard__error-box"> | ||
| <div class="migration-wizard__error-box-header"> | ||
| <div class="migration-wizard__error-box-title"> | ||
| {language.t("migration.error.sessionFailed")} | ||
| </div> | ||
| <button | ||
| type="button" | ||
| class="migration-wizard__copy-btn" | ||
| onClick={() => void copySessionError(groupMessage("sessions")!)} | ||
| > | ||
| {language.t("migration.error.action.copy")} | ||
| </button> | ||
| </div> | ||
| <div class="migration-wizard__error-text">{groupMessage("sessions")}</div> | ||
| </div> | ||
| </Show> | ||
| </div> | ||
| </div> | ||
| </Show> | ||
|
|
@@ -768,6 +805,7 @@ const MigrationWizard: Component<MigrationWizardProps> = (props) => { | |
| </Show> | ||
|
|
||
| {/* Cleanup option after done */} | ||
| {/* | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why is this code commented out?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Shows the following button - Which deletes legacy settings. I think the cautious move for now is to not have it in place. Screen.Recording.2026-03-31.at.17.48.54.mov |
||
| <Show when={phase() === "done"}> | ||
| <div class="migration-wizard__divider" /> | ||
| <div class="migration-wizard__item migration-wizard__item--clickable"> | ||
|
|
@@ -787,6 +825,7 @@ const MigrationWizard: Component<MigrationWizardProps> = (props) => { | |
| </div> | ||
| </div> | ||
| </Show> | ||
| */} | ||
| </div> | ||
| </Show> | ||
|
|
||
|
|
@@ -818,6 +857,18 @@ const MigrationWizard: Component<MigrationWizardProps> = (props) => { | |
| {language.t("migration.migrate.button")} | ||
| </button> | ||
| </Show> | ||
| <Show when={phase() === "error"}> | ||
| <button | ||
| type="button" | ||
| class="migration-wizard__btn migration-wizard__btn--primary" | ||
| onClick={() => { | ||
| vscode.postMessage({ type: "loadSessions" }) | ||
| setPhase("done") | ||
| }} | ||
| > | ||
| {language.t("migration.error.continue")} | ||
| </button> | ||
| </Show> | ||
| <Show when={phase() === "done"}> | ||
| <button type="button" class="migration-wizard__btn migration-wizard__btn--primary" onClick={handleDone}> | ||
| {language.t("migration.complete.done")} | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.