{"record":{"id":"a3c5c01482140220","repo":"cjpais/Handy","slug":"result-error","errorCode":null,"errorMessage":"result.error","messagePattern":"result\\.error","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"src/stores/settingsStore.ts","lineNumber":384,"sourceCode":"          settings: state.settings\n            ? {\n                ...state.settings,\n                bindings: {\n                  ...state.settings.bindings,\n                  [id]: {\n                    ...state.settings.bindings?.[id]!,\n                    current_binding: binding,\n                  },\n                },\n              }\n            : null,\n        }));\n\n        const result = await commands.changeBinding(id, binding);\n\n        // Check if the command executed successfully\n        if (result.status === \"error\") {\n          throw new Error(result.error);\n        }\n\n        // Check if the binding change was successful\n        if (!result.data.success) {\n          throw new Error(result.data.error || \"Failed to update binding\");\n        }\n      } catch (error) {\n        console.error(`Failed to update binding ${id}:`, error);\n\n        // Rollback on error\n        if (originalBinding && get().settings) {\n          set((state) => ({\n            settings: state.settings\n              ? {\n                  ...state.settings,\n                  bindings: {\n                    ...state.settings.bindings,\n                    [id]: {","sourceCodeStart":366,"sourceCodeEnd":402,"githubUrl":"https://github.com/cjpais/Handy/blob/fbd4e15fa14a721c66c57006ae110428b9e255b3/src/stores/settingsStore.ts#L366-L402","documentation":"This error is thrown after the tauri-specta generated command `commands.changeBinding(id, binding)` resolves with `{ status: \"error\" }`. In Tauri 2, a command whose Rust handler returns `Err(String)` (or panics) is serialized into exactly this shape, so `result.error` carries the backend's rejection string. It means the command failed at the IPC level before producing a BindingResponse — for example `change_binding` in src-tauri/src/shortcut/mod.rs returns `Err(\"Binding cannot be empty\")` for a blank shortcut. It is distinct from error [1], where the command succeeds but the backend reports success:false.","triggerScenarios":"Passing an empty or whitespace-only binding string (rejected at shortcut/mod.rs:119); the Rust handler returning Err (e.g. settings store write failure); a stale bindings.ts that sends a payload shape the backend no longer accepts; a command panic serialized as an error string.","commonSituations":"UI lets the user clear a shortcut field and save before validation runs; frontend/backend binding drift after editing change_binding without regenerating bindings.ts; tauri-plugin-store write failures (locked or corrupted settings file).","solutions":["Log the full result.error string and check the backend log — change_binding warn!-logs the reason right before returning Err","Guard in the UI: refuse to call changeBinding with an empty or whitespace-only binding string","If the backend signature changed, regenerate tauri-specta bindings (bindings.ts) and rebuild","Verify the settings store file is writable and valid JSON; reset it if corrupted"],"exampleFix":"// before\nconst result = await commands.changeBinding(id, binding);\nif (result.status === \"error\") {\n  throw new Error(result.error);\n}\n\n// after — never send an empty binding, and keep the command + id in the thrown message\nif (!binding.trim()) {\n  throw new Error(`Refusing empty binding for ${id}`);\n}\nconst result = await commands.changeBinding(id, binding);\nif (result.status === \"error\") {\n  throw new Error(`changeBinding(${id}) command failed: ${result.error}`);\n}","handlingStrategy":"try-catch","validationCode":"// before invoking, confirm the id exists in the current settings map\nconst bindings = get().settings?.bindings;\nif (!bindings || !(id in bindings)) {\n  throw new Error(`Unknown binding id: ${id}`);\n}\nif (!binding.trim()) {\n  throw new Error(`Binding for ${id} cannot be empty`);\n}","typeGuard":"export function isCommandFailure(\n  r: Result<unknown, string>\n): r is { status: \"error\"; error: string } {\n  return r.status === \"error\";\n}","tryCatchPattern":"try {\n  const result = await commands.changeBinding(id, binding);\n  if (result.status === \"error\") throw new Error(result.error);\n  if (!result.data.success) throw new Error(result.data.error ?? \"Failed to update binding\");\n} catch (error) {\n  // settingsStore already rolls back the optimistic binding here — keep that\n  console.error(`Failed to update binding ${id}:`, error);\n  // surface result.error text to the UI toast, not the generic message\n}","preventionTips":["Validate binding strings client-side (non-empty, no whitespace-only) before every changeBinding call","Keep the optimistic-update/rollback pair (already in settingsStore.ts) so UI never desyncs from backend state","Regenerate bindings.ts via tauri-specta whenever change_binding's signature or error strings change"],"tags":["tauri","typescript","ipc","keyboard-bindings","shortcuts"],"backgroundTag":"tauri-command-rejected","analyzedSha":"fbd4e15fa14a721c66c57006ae110428b9e255b3","analyzedAt":"2026-08-17T10:29:55.597Z","contentChangedAt":"2026-08-17T10:29:55.597Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}