{"record":{"id":"508769ed07db2fb1","repo":"tonhowtf/omniget","slug":"t-common-error-mapped-ai-not-configured-t-downloads-sw-not","errorCode":null,"errorMessage":"$t('common.error') | mapped: ai_not_configured -> $t('downloads.sw.not_configured'), translation_mismatch -> $t('downloads.sw.translate_mismatch'), grammar_mismatch -> $t('downloads.sw.grammar_mismatch')","messagePattern":"\\$t\\('common\\.error'\\) \\| mapped: ai_not_configured -> \\$t\\('downloads\\.sw\\.not_configured'\\), translation_mismatch -> \\$t\\('downloads\\.sw\\.translate_mismatch'\\), grammar_mismatch -> \\$t\\('downloads\\.sw\\.grammar_mismatch'\\)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"src/components/downloads/SubtitleWorkshop.svelte","lineNumber":27,"sourceCode":"  type Cue = { start_ms: number; end_ms: number; text: string };\n\n  let cues = $state<Cue[]>([]);\n  let loadedPath = $state(\"\");\n  let busy = $state(false);\n  let shiftMs = $state(0);\n  let findText = $state(\"\");\n  let replaceText = $state(\"\");\n  let targetLang = $state(\"\");\n  let grammarStyle = $state<\"original\" | \"formal\" | \"casual\">(\"original\");\n  let tpOldA = $state(\"\");\n  let tpNewA = $state(\"\");\n  let tpOldB = $state(\"\");\n  let tpNewB = $state(\"\");\n  let peaks = $state<number[]>([]);\n  let shots = $state<number[]>([]);\n  let durationMs = $derived(cues.length ? cues[cues.length - 1].end_ms : 0);\n\n  function err(e: unknown) {\n    const raw = typeof e === \"string\" ? e : ($t(\"common.error\") as string);\n    const msg =\n      raw === \"ai_not_configured\"\n        ? ($t(\"downloads.sw.not_configured\") as string)\n        : raw === \"translation_mismatch\"\n          ? ($t(\"downloads.sw.translate_mismatch\") as string)\n          : raw === \"grammar_mismatch\"\n            ? ($t(\"downloads.sw.grammar_mismatch\") as string)\n            : raw;\n    showToast(\"error\", msg);\n  }\n\n  function fmt(ms: number): string {\n    const s = Math.floor(ms / 1000);\n    const h = Math.floor(s / 3600);\n    const m = Math.floor((s % 3600) / 60);\n    const sec = s % 60;\n    const milli = ms % 1000;","sourceCodeStart":9,"sourceCodeEnd":45,"githubUrl":"https://github.com/tonhowtf/omniget/blob/8600b91f4246848bac346874daa9e61c1fc5677a/src/components/downloads/SubtitleWorkshop.svelte#L9-L45","documentation":"SubtitleWorkshop's err() is the shared error-mapper used by all invoke call-sites in the component. It takes the raw error (usually a string error code from the Rust backend), falls back to the localized common.error label when the error isn't a string, and maps known codes like ai_not_configured, translation_mismatch and grammar_mismatch to user-facing localized messages. The 'error message' here is that mapping contract, not a throw itself.","triggerScenarios":"err(e) is invoked from loadFile, loadMedia, translateAll, grammarFix or saveAs when a Tauri invoke rejects with a raw code string; if the code is 'ai_not_configured', 'translation_mismatch' or 'grammar_mismatch' it is translated, any non-string error falls back to the generic $t('common.error') text.","commonSituations":"Backend adds a new error code that err() doesn't map, so users see a generic message; an invoke rejects with an Error object instead of a string, losing the specific reason; translations for the sw.* keys missing from a locale file show raw keys.","solutions":["Extend err()'s mapping table whenever the backend adds a new error code string.","Ensure invoke rejections are plain strings (backend should return string error codes, not throw Error objects).","Verify downloads.sw.* and common.error keys exist in every locale bundle.","Log the unmapped raw error to console so unknown codes can be discovered and mapped."],"exampleFix":"// before\nconst raw = typeof e === \"string\" ? e : ($t(\"common.error\") as string);\n// after\nconst raw = typeof e === \"string\" ? e : ((e as Error)?.message ?? ($t(\"common.error\") as string));\nconsole.warn(\"unmapped subtitle workshop error:\", e);","handlingStrategy":"type-guard","validationCode":"const KNOWN_CODES = [\"ai_not_configured\", \"translation_mismatch\", \"grammar_mismatch\"] as const;\n// keep backend error-code list exported from a shared module and diff it against KNOWN_CODES in a test","typeGuard":"function isBackendErrorCode(v: unknown): v is string {\n  return typeof v === \"string\" && v.length > 0 && !v.includes(\" \");\n}","tryCatchPattern":"function err(e: unknown) {\n  const raw = typeof e === \"string\"\n    ? e\n    : ((e as Error)?.message ?? ($t(\"common.error\") as string));\n  if (!KNOWN_CODES.includes(raw as any)) console.warn(\"unmapped error code:\", raw);\n  showToast(\"error\", mapCodeToMessage(raw));\n}","preventionTips":["Maintain a single shared source of truth for backend error codes and test the mapper against it.","Always reject with plain string codes from Rust commands consumed by this component.","Add fallback text for every new sw.* i18n key in all locales.","Log unmapped codes so gaps in the mapping table surface in development."],"tags":["i18n","error-mapping","tauri","invoke"],"backgroundTag":"unexpected-response-shape","analyzedSha":"8600b91f4246848bac346874daa9e61c1fc5677a","analyzedAt":"2026-09-12T14:29:19.317Z","contentChangedAt":"2026-09-12T14:29:19.317Z","schemaVersion":2},"datasetVersion":"2026-09-15T23:17:13.987Z"}