{"record":{"id":"5ce77c196bf632db","repo":"jlcodes99/cockpit-tools","slug":"tauri-save-failed-falling-back-to-web-download","errorCode":null,"errorMessage":"Tauri save failed, falling back to web download","messagePattern":"Tauri save failed, falling back to web download","errorType":"console","errorClass":null,"httpStatus":null,"severity":"info","filePath":"src/components/MfaVaultManager.tsx","lineNumber":336,"sourceCode":"        secret: record.secret,\n        time: record.time,\n      }));\n\n      const dataStr = JSON.stringify(exportList, null, 2);\n      const defaultFilename = `twofa_saved_export_${new Date().toISOString().split('T')[0]}.json`;\n\n      try {\n        const filePath = await save({\n          filters: [{ name: 'JSON', extensions: ['json'] }],\n          defaultPath: defaultFilename,\n        });\n\n        if (filePath) {\n          await writeTextFile(filePath, dataStr);\n        }\n        return;\n      } catch (e) {\n        console.warn('Tauri save failed, falling back to web download', e);\n      }\n\n      const blob = new Blob([dataStr], { type: 'application/json' });\n      const url = URL.createObjectURL(blob);\n      const a = document.createElement('a');\n      a.href = url;\n      a.download = defaultFilename;\n      document.body.appendChild(a);\n      a.click();\n      document.body.removeChild(a);\n      URL.revokeObjectURL(url);\n    } catch {}\n  };\n\n  const handleImport = async () => {\n    try {\n      let dataStr = '';\n      try {","sourceCodeStart":318,"sourceCodeEnd":354,"githubUrl":"https://github.com/jlcodes99/cockpit-tools/blob/1ed8b77992d62ca81fabf744deb0839ad361d5bf/src/components/MfaVaultManager.tsx#L318-L354","documentation":"In MfaVaultManager's handleExport, the export first tries the native path: Tauri's save dialog (dialog.save) plus writeTextFile to the chosen path. If any part throws (dialog unavailable, write permission denied, not running under Tauri), it logs this warning and falls back to a browser-style Blob + anchor download. The export still succeeds via the fallback in most cases.","triggerScenarios":"The Tauri API call fails during MFA vault export: dialog plugin not registered/permitted, chosen path unwritable, save cancelled resulting in null handled as error, or the code running in a plain web context where @tauri-apps/api is unavailable.","commonSituations":"Running the frontend in a browser during development (no Tauri runtime), missing tauri-plugin-dialog/fs in capabilities config, OS denying write to the selected directory, or CSP blocking the plugin invoke.","solutions":["Check the logged `e` — if in a browser/dev server, the fallback download is expected behavior.","Ensure tauri-plugin-dialog and tauri-plugin-fs are installed and allowlisted in the Tauri capabilities config.","Verify the selected save directory is writable (permissions, cloud-sync placeholders, antivirus).","Use the fallback download that already triggered and confirm the exported JSON file is intact."],"exampleFix":"// tauri.conf.json capabilities\n// before\n\"permissions\": []\n// after\n\"permissions\": [\n  \"dialog:default\",\n  \"fs:allow-write-text-file\"\n]","handlingStrategy":"fallback","validationCode":"// detect Tauri runtime before attempting native save\nconst isTauri = '__TAURI_INTERNALS__' in window;\nif (!isTauri) {\n  triggerWebDownload(dataStr, fileName);\n  return;\n}\n","typeGuard":"function isTauriEnvironment(): boolean {\n  return typeof window !== 'undefined' && '__TAURI_INTERNALS__' in window;\n}\n","tryCatchPattern":"try {\n  const filePath = await save({ defaultPath: fileName });\n  if (filePath) await writeTextFile(filePath, dataStr);\n  return;\n} catch (e) {\n  console.warn('Tauri save failed, falling back to web download', e);\n}\ntriggerWebDownload(dataStr, fileName); // Blob + anchor fallback","preventionTips":["Register dialog and fs plugins in Tauri capabilities config","Feature-detect the Tauri runtime in dev/browser modes","Revoke the object URL after fallback download (URL.revokeObjectURL) to avoid leaks","Verify export JSON parses back correctly after download"],"tags":["tauri","filesystem","export","fallback"],"backgroundTag":"tauri-plugin-permission-denied","analyzedSha":"1ed8b77992d62ca81fabf744deb0839ad361d5bf","analyzedAt":"2026-09-05T09:51:41.178Z","contentChangedAt":"2026-09-05T09:51:41.178Z","schemaVersion":2},"datasetVersion":"2026-09-12T12:17:11.808Z"}