diff --git a/frontend/src/views/settings/SettingsView.vue b/frontend/src/views/settings/SettingsView.vue
index 48946c7..3aac2f3 100644
--- a/frontend/src/views/settings/SettingsView.vue
+++ b/frontend/src/views/settings/SettingsView.vue
@@ -242,10 +242,13 @@
-
+
{{ syncResultMessage }}
+
+ {{ errorMessage }}
+
@@ -341,6 +344,10 @@ const deleteLoading = ref(false)
const showSyncResult = ref(false)
const syncResultMessage = ref('')
+// Error display
+const showError = ref(false)
+const errorMessage = ref('')
+
// Sync logs
const syncLogs = ref([])
const logsLoading = ref(false)
@@ -420,6 +427,13 @@ async function startPlaidLink() {
try {
const { data: linkData } = await bankSyncApi.createLinkToken()
+ if (!linkData.linkToken) {
+ errorMessage.value = 'Plaid returned an empty link token. Check your Plaid credentials in appsettings.json.'
+ showError.value = true
+ plaidLoading.value = false
+ return
+ }
+
// Dynamically load Plaid Link script
await loadPlaidScript()
@@ -431,8 +445,9 @@ async function startPlaidLink() {
connections.value.unshift(conn)
syncIntervals[conn.id] = conn.syncIntervalMinutes
openMappingDialog(conn)
- } catch (err) {
- console.error('Failed to complete Plaid link:', err)
+ } catch (err: any) {
+ errorMessage.value = err.response?.data?.error || 'Failed to complete Plaid link'
+ showError.value = true
}
},
onExit: () => {
@@ -440,8 +455,9 @@ async function startPlaidLink() {
}
})
plaid.open()
- } catch (err) {
- console.error('Failed to create link token:', err)
+ } catch (err: any) {
+ errorMessage.value = err.response?.data?.error || 'Failed to create Plaid link token. Check your Plaid credentials.'
+ showError.value = true
plaidLoading.value = false
}
}
@@ -469,8 +485,9 @@ async function connectSimpleFin() {
showSimpleFinDialog.value = false
simpleFinToken.value = ''
openMappingDialog(conn)
- } catch (err) {
- console.error('Failed to connect SimpleFIN:', err)
+ } catch (err: any) {
+ errorMessage.value = err.response?.data?.error || 'Failed to connect via SimpleFIN. Check your setup token.'
+ showError.value = true
} finally {
simpleFinLoading.value = false
}
@@ -540,8 +557,9 @@ async function syncNow(conn: SyncConnection) {
syncResultMessage.value = `Synced ${conn.institutionName}: ${data.transactionsImported} imported, ${data.transactionsSkipped} skipped`
showSyncResult.value = true
await loadConnections()
- } catch (err) {
- console.error('Sync failed:', err)
+ } catch (err: any) {
+ errorMessage.value = err.response?.data?.error || 'Sync failed'
+ showError.value = true
} finally {
syncingConnections[conn.id] = false
}
diff --git a/src/Purrse.Plugins.BankSync/PlaidSyncProvider.cs b/src/Purrse.Plugins.BankSync/PlaidSyncProvider.cs
index c3e941d..15895f4 100644
--- a/src/Purrse.Plugins.BankSync/PlaidSyncProvider.cs
+++ b/src/Purrse.Plugins.BankSync/PlaidSyncProvider.cs
@@ -43,6 +43,10 @@ public class PlaidSyncProvider : IAccountSyncProvider
public async Task CreateLinkTokenAsync(string userId)
{
+ if (string.IsNullOrEmpty(_config.ClientId) || string.IsNullOrEmpty(_config.Secret))
+ throw new InvalidOperationException(
+ "Plaid credentials are not configured. Set BankSync:Plaid:ClientId and BankSync:Plaid:Secret in appsettings.json.");
+
var response = await _client.LinkTokenCreateAsync(new LinkTokenCreateRequest
{
ClientId = _config.ClientId,