diff options
| author | OpeOginni <[email protected]> | 2026-03-05 13:28:17 +0100 |
|---|---|---|
| committer | GitHub <[email protected]> | 2026-03-05 06:28:17 -0600 |
| commit | 27baa2d65cfa100283bda334e80244d6d8c440fb (patch) | |
| tree | db897fd42063652865ab28ad788412c557fc7eff /packages/app/src/utils/server-errors.ts | |
| parent | 62909e917ada44f64bf46fb38936bc99357cb63c (diff) | |
| download | opencode-27baa2d65cfa100283bda334e80244d6d8c440fb.tar.gz opencode-27baa2d65cfa100283bda334e80244d6d8c440fb.zip | |
refactor(desktop): improve error handling and translation in server error formatting (#16171)
Diffstat (limited to 'packages/app/src/utils/server-errors.ts')
| -rw-r--r-- | packages/app/src/utils/server-errors.ts | 75 |
1 files changed, 53 insertions, 22 deletions
diff --git a/packages/app/src/utils/server-errors.ts b/packages/app/src/utils/server-errors.ts index 85ebca132..2c3a8c54d 100644 --- a/packages/app/src/utils/server-errors.ts +++ b/packages/app/src/utils/server-errors.ts @@ -7,28 +7,31 @@ export type ConfigInvalidError = { } } -type Label = { - unknown: string - invalidConfiguration: string +export type ProviderModelNotFoundError = { + name: "ProviderModelNotFoundError" + data: { + providerID: string + modelID: string + suggestions?: string[] + } } -const fallback: Label = { - unknown: "Unknown error", - invalidConfiguration: "Invalid configuration", -} +type Translator = (key: string, vars?: Record<string, string | number>) => string -function resolveLabel(labels: Partial<Label> | undefined): Label { - return { - unknown: labels?.unknown ?? fallback.unknown, - invalidConfiguration: labels?.invalidConfiguration ?? fallback.invalidConfiguration, - } +function tr(translator: Translator | undefined, key: string, text: string, vars?: Record<string, string | number>) { + if (!translator) return text + const out = translator(key, vars) + if (!out || out === key) return text + return out } -export function formatServerError(error: unknown, labels?: Partial<Label>) { - if (isConfigInvalidErrorLike(error)) return parseReabaleConfigInvalidError(error, labels) +export function formatServerError(error: unknown, translate?: Translator, fallback?: string) { + if (isConfigInvalidErrorLike(error)) return parseReadableConfigInvalidError(error, translate) + if (isProviderModelNotFoundErrorLike(error)) return parseReadableProviderModelNotFoundError(error, translate) if (error instanceof Error && error.message) return error.message if (typeof error === "string" && error) return error - return resolveLabel(labels).unknown + if (fallback) return fallback + return tr(translate, "error.chain.unknown", "Unknown error") } function isConfigInvalidErrorLike(error: unknown): error is ConfigInvalidError { @@ -37,13 +40,41 @@ function isConfigInvalidErrorLike(error: unknown): error is ConfigInvalidError { return o.name === "ConfigInvalidError" && typeof o.data === "object" && o.data !== null } -export function parseReabaleConfigInvalidError(errorInput: ConfigInvalidError, labels?: Partial<Label>) { - const head = resolveLabel(labels).invalidConfiguration - const file = errorInput.data.path && errorInput.data.path !== "config" ? errorInput.data.path : "" +function isProviderModelNotFoundErrorLike(error: unknown): error is ProviderModelNotFoundError { + if (typeof error !== "object" || error === null) return false + const o = error as Record<string, unknown> + return o.name === "ProviderModelNotFoundError" && typeof o.data === "object" && o.data !== null +} + +export function parseReadableConfigInvalidError(errorInput: ConfigInvalidError, translator?: Translator) { + const file = errorInput.data.path && errorInput.data.path !== "config" ? errorInput.data.path : "config" const detail = errorInput.data.message?.trim() ?? "" - const issues = (errorInput.data.issues ?? []).map((issue) => { - return `${issue.path.join(".")}: ${issue.message}` + const issues = (errorInput.data.issues ?? []) + .map((issue) => { + const msg = issue.message.trim() + if (!issue.path.length) return msg + return `${issue.path.join(".")}: ${msg}` + }) + .filter(Boolean) + const msg = issues.length ? issues.join("\n") : detail + if (!msg) return tr(translator, "error.chain.configInvalid", `Config file at ${file} is invalid`, { path: file }) + return tr(translator, "error.chain.configInvalidWithMessage", `Config file at ${file} is invalid: ${msg}`, { + path: file, + message: msg, }) - if (issues.length) return [head, file, "", ...issues].filter(Boolean).join("\n") - return [head, file, detail].filter(Boolean).join("\n") +} + +function parseReadableProviderModelNotFoundError(errorInput: ProviderModelNotFoundError, translator?: Translator) { + const p = errorInput.data.providerID.trim() + const m = errorInput.data.modelID.trim() + const list = (errorInput.data.suggestions ?? []).map((v) => v.trim()).filter(Boolean) + const body = tr(translator, "error.chain.modelNotFound", `Model not found: ${p}/${m}`, { provider: p, model: m }) + const tail = tr(translator, "error.chain.checkConfig", "Check your config (opencode.json) provider/model names") + if (list.length) { + const suggestions = list.slice(0, 5).join(", ") + return [body, tr(translator, "error.chain.didYouMean", `Did you mean: ${suggestions}`, { suggestions }), tail].join( + "\n", + ) + } + return [body, tail].join("\n") } |
